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;
7use std::cmp::Ordering;
8#[allow(clippy::disallowed_types)]
9use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
10use std::hash::{BuildHasherDefault, Hash, Hasher};
11use std::rc::Rc;
12
13/// Rotate constant copied from rustc-hash / `FxHash`. The default
14/// `RandomState` hasher seeds itself from the OS RNG and runs `SipHash` on
15/// every key, which dominates `recognize_state_fast`'s memo lookups;
16/// `FxHasher` is a streaming integer hasher with near-zero per-call overhead
17/// and matches the access pattern of small integer keys that the parser memo
18/// uses.
19#[derive(Clone, Copy, Default)]
20struct FxHasher {
21    hash: u64,
22}
23
24const FX_ROT: u32 = 5;
25const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
26
27impl Hasher for FxHasher {
28    /// Folds bytes 8 at a time so a `write(&[u8; 8])` call hashes to the same
29    /// state as a `write_u64` of the same little-endian bits. The `Hash` impls
30    /// for `String`, `[u8; N]`, and slice-like types reach the hasher through
31    /// `write`; matching the typed-method behaviour avoids the silent
32    /// divergence flagged in PR #5 review (Greptile P2). Tail bytes that do
33    /// not form a full word are mixed one at a time with the same constants,
34    /// keeping behaviour deterministic regardless of the slice length.
35    #[inline]
36    fn write(&mut self, mut bytes: &[u8]) {
37        while bytes.len() >= 8 {
38            let (head, rest) = bytes.split_at(8);
39            let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
40            self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
41            bytes = rest;
42        }
43        for byte in bytes {
44            self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(*byte)).wrapping_mul(FX_SEED);
45        }
46    }
47    #[inline]
48    fn write_u64(&mut self, value: u64) {
49        self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
50    }
51    #[inline]
52    fn write_usize(&mut self, value: usize) {
53        self.write_u64(value as u64);
54    }
55    #[inline]
56    fn write_u32(&mut self, value: u32) {
57        self.write_u64(u64::from(value));
58    }
59    #[inline]
60    fn write_i32(&mut self, value: i32) {
61        self.write_u64(u64::from(i32::cast_unsigned(value)));
62    }
63    #[inline]
64    fn finish(&self) -> u64 {
65        self.hash
66    }
67}
68
69type FxBuildHasher = BuildHasherDefault<FxHasher>;
70#[allow(clippy::disallowed_types)]
71type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
72#[allow(clippy::disallowed_types)]
73type FxHashSet<K> = HashSet<K, FxBuildHasher>;
74
75use crate::atn::AtnStateKind;
76use crate::atn::parser::{
77    ParserAtnPrediction, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
78};
79use crate::atn::parser_atn::{
80    ParserAtn as Atn, ParserAtnState as AtnState, ParserIntervalSet, ParserTransition,
81    ParserTransitionData as Transition, ParserTransitionKind,
82};
83#[cfg(test)]
84use crate::atn::parser_atn::{ParserAtnBuilder, ParserTransitionSpec};
85use crate::char_stream::CharStream;
86use crate::errors::AntlrError;
87use crate::int_stream::IntStream;
88use crate::lexer::{LexerCustomAction, LexerLifecycleCtx, LexerSemCtx};
89use crate::recognizer::{Recognizer, RecognizerData};
90use crate::semir::{self, AStmt, ArithOp, CmpOp, ExprId, HookId, PExpr, SemIr, StmtId};
91use crate::token::{
92    TOKEN_EOF, Token, TokenId, TokenSource, TokenSourceError, TokenSpec, TokenStore, TokenView,
93};
94use crate::token_stream::CommonTokenStream;
95use crate::tree::{
96    Node, NodeId, ParseTreeCheckpoint, ParseTreeStorage, ParsedFile, ParserRuleContext,
97};
98use crate::vocabulary::Vocabulary;
99
100type ParseTree = NodeId;
101
102/// Upper bound for the recursive metadata recognizer before it treats a path as
103/// non-viable. Long expression-regression descriptors legitimately walk tens
104/// of thousands of ATN edges.
105const RECOGNITION_DEPTH_LIMIT: usize = 32_768;
106/// Preserve the recursive hot path while checking native stack capacity often
107/// enough that one unchecked group cannot cross the protected red zone.
108const FAST_RECOGNIZE_STACK_CHECK_INTERVAL: usize = 8;
109const FAST_RECOGNIZE_RED_ZONE: usize = 1024 * 1024;
110const FAST_RECOGNIZE_STACK_SIZE: usize = 4 * 1024 * 1024;
111/// Whole-rule direct adaptive execution is allowed to give up and fall back to
112/// the existing recognizer. Keep the guard at the same order of magnitude as
113/// speculative recognition so malformed cyclic ATNs cannot spin forever.
114const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
115/// Probe window for deciding whether clean-pass memo entries are reusable
116/// enough to keep caching. High-cardinality parses mostly produce one-shot
117/// entries; compact ambiguous loops repeatedly hit the same keys.
118const CLEAN_MEMO_PROBE_LIMIT: usize = 4096;
119const CLEAN_MEMO_REPEAT_LIMIT: usize = 8;
120/// Sparse parses periodically reopen the bounded probe so a repeat-heavy
121/// region that starts later in the token stream can promote memoization.
122const CLEAN_MEMO_REPROBE_INTERVAL: usize = 262_144;
123const FAST_RECOGNIZE_VISITING_CAPACITY: usize = 256;
124const FAST_RECOGNIZE_MIN_MEMO_CAPACITY: usize = 256;
125const FAST_RECOGNIZE_MAX_MEMO_CAPACITY: usize = 524_288;
126const FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY: usize = 65_536;
127
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129enum CleanMemoMode {
130    Probe,
131    Promote,
132    Sparse,
133}
134
135fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
136    intervals
137        .iter()
138        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
139}
140
141fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
142    let mut symbols = BTreeSet::new();
143    for (start, stop) in intervals {
144        symbols.extend(*start..=*stop);
145    }
146    symbols
147}
148
149fn interval_complement_symbols(
150    intervals: &[(i32, i32)],
151    min_vocabulary: i32,
152    max_vocabulary: i32,
153) -> BTreeSet<i32> {
154    (min_vocabulary..=max_vocabulary)
155        .filter(|symbol| !interval_set_contains(intervals, *symbol))
156        .collect()
157}
158
159#[cfg(feature = "perf-counters")]
160mod perf_counters {
161    use std::cell::Cell;
162    thread_local! {
163        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
164        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
165        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
166        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
167        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
168        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
169        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
170        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
171        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
172        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
173        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
174        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
175        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
176    }
177    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
178        c.with(|v| v.set(v.get() + n));
179    }
180    thread_local! {
181        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
182        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
183        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
184        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
185        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
186        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
187        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
188        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
189        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
190        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
191        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
192    }
193    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
194        [
195            ("rfs_calls", RFS_CALLS.with(Cell::get)),
196            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
197            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
198            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
199            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
200            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
201            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
202            (
203                "outcome_dedupe_inputs",
204                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
205            ),
206            (
207                "outcome_dedupe_removed",
208                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
209            ),
210            (
211                "outcome_dedupe_inline",
212                OUTCOME_DEDUPE_INLINE.with(Cell::get),
213            ),
214            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
215            (
216                "outcome_dedupe_sparse",
217                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
218            ),
219            (
220                "outcome_dedupe_dense_words",
221                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
222            ),
223            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
224            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
225            (
226                "atom_range_transitions",
227                ATOM_RANGE_TRANSITIONS.with(Cell::get),
228            ),
229            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
230            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
231            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
232            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
233            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
234            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
235            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
236            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
237        ]
238    }
239    pub fn reset() {
240        RFS_CALLS.with(|c| c.set(0));
241        RFS_MEMO_HITS.with(|c| c.set(0));
242        RFS_MEMO_MISSES.with(|c| c.set(0));
243        RFS_VISITING_CYCLE.with(|c| c.set(0));
244        MEMO_INSERTED.with(|c| c.set(0));
245        OUTCOMES_PUSHED.with(|c| c.set(0));
246        OUTCOMES_CLONED.with(|c| c.set(0));
247        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
248        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
249        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
250        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
251        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
252        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
253        EPSILON_TRANSITIONS.with(|c| c.set(0));
254        RULE_TRANSITIONS.with(|c| c.set(0));
255        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
256        SINGLE_TRANS_BODY.with(|c| c.set(0));
257        MULTI_TRANS_BODY.with(|c| c.set(0));
258        SINGLE_TRANS_RULE.with(|c| c.set(0));
259        SINGLE_TRANS_ATOM.with(|c| c.set(0));
260        SINGLE_TRANS_OTHER.with(|c| c.set(0));
261        OUTCOMES_RETURN_0.with(|c| c.set(0));
262        OUTCOMES_RETURN_1.with(|c| c.set(0));
263        OUTCOMES_RETURN_N.with(|c| c.set(0));
264    }
265    pub fn dump() {
266        for (name, value) in snapshot() {
267            #[allow(clippy::print_stderr)]
268            {
269                eprintln!("perf {name}={value}");
270            }
271        }
272    }
273}
274
275#[cfg(feature = "perf-counters")]
276pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
277/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
278/// fast recognizer has probed far enough that per-token stream sync dominates.
279/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
280/// switching long inputs to the cheaper filled-stream path before large fanout.
281const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
282/// Parser semantic action reached while recognizing one ATN path.
283///
284/// Generated parsers use `source_state` to dispatch back to the grammar action
285/// rendered for that ATN action transition. The token interval is the current
286/// rule's input span at the action site, which covers common target templates
287/// such as `$text`. Rule-init actions do not have an ATN action source state,
288/// so they are marked separately and may carry an ATN state for expected-token
289/// rendering.
290#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
291pub struct ParserAction {
292    source_state: usize,
293    rule_index: usize,
294    start_index: usize,
295    stop_index: Option<usize>,
296    rule_init: bool,
297    expected_state: Option<usize>,
298}
299
300impl ParserAction {
301    /// Creates an action event for a recognized parser path.
302    pub const fn new(
303        source_state: usize,
304        rule_index: usize,
305        start_index: usize,
306        stop_index: Option<usize>,
307    ) -> Self {
308        Self {
309            source_state,
310            rule_index,
311            start_index,
312            stop_index,
313            rule_init: false,
314            expected_state: None,
315        }
316    }
317
318    /// Creates an action event for a rule-level `@init` action.
319    pub const fn new_rule_init(
320        rule_index: usize,
321        start_index: usize,
322        expected_state: Option<usize>,
323    ) -> Self {
324        Self {
325            source_state: usize::MAX,
326            rule_index,
327            start_index,
328            stop_index: None,
329            rule_init: true,
330            expected_state,
331        }
332    }
333
334    /// ATN state that owns the semantic-action transition.
335    pub const fn source_state(&self) -> usize {
336        self.source_state
337    }
338
339    /// Grammar rule index recorded by the serialized ATN action transition.
340    pub const fn rule_index(&self) -> usize {
341        self.rule_index
342    }
343
344    /// Token-stream index where the active rule began.
345    pub const fn start_index(&self) -> usize {
346        self.start_index
347    }
348
349    /// Last token-stream index consumed before the action was reached.
350    pub const fn stop_index(&self) -> Option<usize> {
351        self.stop_index
352    }
353
354    /// Reports whether this event represents a rule-level `@init` action.
355    pub const fn is_rule_init(&self) -> bool {
356        self.rule_init
357    }
358
359    /// ATN state used to compute expected-token display for this action.
360    pub const fn expected_state(&self) -> Option<usize> {
361        self.expected_state
362    }
363}
364
365/// Runtime view passed to parser semantic hooks.
366///
367/// The context is intentionally read-only with respect to parser structure:
368/// predicates may run speculatively during prediction, and hooks can be called
369/// more than once for paths that are later abandoned. Lookahead methods may
370/// buffer tokens from the underlying token source, matching normal parser
371/// prediction behavior.
372pub struct ParserSemCtx<'a, S>
373where
374    S: TokenSource,
375{
376    input: &'a mut CommonTokenStream<S>,
377    tree_storage: &'a ParseTreeStorage,
378    rule_index: usize,
379    coordinate_index: usize,
380    rule_name: Option<String>,
381    context: Option<&'a ParserRuleContext>,
382    tree: Option<ParseTree>,
383    local_int_arg: Option<(usize, i64)>,
384    member_values: &'a BTreeMap<usize, i64>,
385    action: Option<ParserAction>,
386}
387
388impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
389where
390    S: TokenSource,
391{
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        f.debug_struct("ParserSemCtx")
394            .field("rule_index", &self.rule_index)
395            .field("coordinate_index", &self.coordinate_index)
396            .field("rule_name", &self.rule_name)
397            .field("context", &self.context)
398            .field("tree", &self.tree)
399            .field("local_int_arg", &self.local_int_arg)
400            .field("member_values", &self.member_values)
401            .field("action", &self.action)
402            .finish_non_exhaustive()
403    }
404}
405
406impl<'a, S> ParserSemCtx<'a, S>
407where
408    S: TokenSource,
409{
410    /// Rule index that owns the predicate/action coordinate.
411    #[must_use]
412    pub const fn rule_index(&self) -> usize {
413        self.rule_index
414    }
415
416    /// Rule name that owns the coordinate, when recognizer metadata has it.
417    #[must_use]
418    pub fn rule_name(&self) -> Option<&str> {
419        self.rule_name.as_deref()
420    }
421
422    /// Predicate/action index inside the owning rule. Parser actions keyed only
423    /// by ATN source state report `usize::MAX` here; use [`Self::action`] for
424    /// the stable action event.
425    #[must_use]
426    pub const fn coordinate_index(&self) -> usize {
427        self.coordinate_index
428    }
429
430    /// Current token-stream index.
431    #[must_use]
432    pub fn input_index(&self) -> usize {
433        self.input.index()
434    }
435
436    /// Token type at one-based lookahead/lookbehind offset.
437    pub fn la(&mut self, offset: isize) -> i32 {
438        self.input.la(offset)
439    }
440
441    /// Token at one-based lookahead/lookbehind offset.
442    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
443        self.input.lt(offset)
444    }
445
446    /// Borrowing token view for text inspection at a one-based offset.
447    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
448        self.lt(offset)
449    }
450
451    /// Token at an absolute buffered index, including hidden/custom channels.
452    ///
453    /// Unlike [`Self::lt`], this does not apply the token stream's channel
454    /// filter and does not move its cursor. It is intended for semantic helpers
455    /// such as automatic-semicolon-insertion checks that inspect trivia
456    /// immediately before the current visible token.
457    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
458        self.input.get(index)
459    }
460
461    /// Current generated rule context, when a generated rule predicate supplied
462    /// one.
463    #[must_use]
464    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
465        self.context
466    }
467
468    /// Flat tree storage containing completed children visible to this hook.
469    #[must_use]
470    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
471        self.tree_storage
472    }
473
474    /// Canonical token store used by completed flat-tree nodes.
475    #[must_use]
476    pub const fn token_store(&self) -> &TokenStore {
477        self.input.token_store()
478    }
479
480    /// Completed parse-tree root ID passed to a replayed action hook.
481    #[must_use]
482    pub const fn tree_id(&self) -> Option<NodeId> {
483        self.tree
484    }
485
486    /// Completed parse tree passed to an action hook, if the action is being
487    /// replayed after recognition.
488    #[must_use]
489    pub fn tree(&self) -> Option<Node<'_>> {
490        self.tree
491            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
492    }
493
494    /// Integer local argument visible to this predicate coordinate.
495    #[must_use]
496    pub fn local_int_arg(&self) -> Option<i64> {
497        self.local_int_arg.map(|(_, value)| value)
498    }
499
500    /// Integer member value observed on the current speculative path.
501    #[must_use]
502    pub fn member_int(&self, member: usize) -> Option<i64> {
503        self.member_values.get(&member).copied()
504    }
505
506    /// Parser action event being replayed, when this context belongs to an
507    /// action hook.
508    #[must_use]
509    pub const fn action(&self) -> Option<ParserAction> {
510        self.action
511    }
512
513    /// Text covered by a parser action event.
514    ///
515    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
516    /// EOF the interval ends at the previous *visible* token, so trailing hidden
517    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
518    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
519    /// itself guards `start > stop`, so an empty interval yields `""`.
520    pub fn action_text(&self) -> String {
521        let Some(action) = self.action else {
522            return String::new();
523        };
524        let Some(stop) = action.stop_index() else {
525            return String::new();
526        };
527        let stop = if self
528            .input
529            .get(stop)
530            .is_some_and(|token| token.token_type() == TOKEN_EOF)
531        {
532            let Some(previous) = self.input.previous_visible_token_index(stop) else {
533                return String::new();
534            };
535            previous
536        } else {
537            stop
538        };
539        self.input.text(action.start_index(), stop)
540    }
541}
542
543/// User extension point for parser semantic predicates and actions that the
544/// metadata generator did not translate into built-in runtime metadata.
545///
546/// Returning `None`/`false` says "not handled", so the runtime falls through
547/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
548/// speculative prediction and must be replay-safe.
549pub trait SemanticHooks {
550    /// Whether generated lexers should route lifecycle callbacks through this
551    /// hook object.
552    ///
553    /// User hook implementations opt in by default. [`NoSemanticHooks`]
554    /// overrides this to keep generated lexers on the direct no-extension
555    /// token path.
556    const ENABLES_LEXER_LIFECYCLE: bool = true;
557
558    /// Whether this hook object may observe parser predicate transitions.
559    ///
560    /// Custom hooks default to conservative predicate handling so the fast
561    /// recognizer does not bypass a `sempred` implementation.
562    fn observes_parser_predicates(&self) -> bool {
563        true
564    }
565
566    /// Whether this hook object may override interpreted parser decisions.
567    ///
568    /// This remains disabled by default so ordinary generated parsers retain
569    /// the fast recognizer path.
570    fn observes_parser_decisions(&self) -> bool {
571        false
572    }
573
574    /// Overrides one interpreted parser decision with a one-based alternative.
575    ///
576    /// Returning `None` leaves normal adaptive prediction in control. Hooks
577    /// that return an alternative own any one-shot or input-index filtering
578    /// they require.
579    fn parser_decision_override(
580        &mut self,
581        decision: usize,
582        input_index: usize,
583        alternative_count: usize,
584    ) -> Option<usize> {
585        let _ = (decision, input_index, alternative_count);
586        None
587    }
588
589    fn sempred<S>(
590        &mut self,
591        ctx: &mut ParserSemCtx<'_, S>,
592        rule_index: usize,
593        pred_index: usize,
594    ) -> Option<bool>
595    where
596        S: TokenSource,
597    {
598        let _ = (ctx, rule_index, pred_index);
599        None
600    }
601
602    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
603    where
604        S: TokenSource,
605    {
606        let _ = (ctx, action);
607        false
608    }
609
610    fn lexer_sempred<I>(
611        &mut self,
612        ctx: &mut LexerSemCtx<'_, I>,
613        rule_index: usize,
614        pred_index: usize,
615    ) -> Option<bool>
616    where
617        I: CharStream,
618    {
619        let _ = (ctx, rule_index, pred_index);
620        None
621    }
622
623    /// Runs a lexer custom action on the committed lexing path. Returns whether
624    /// the hook handled the action.
625    ///
626    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
627    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
628    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
629    /// queued prefix tokens, just like the closure-based `custom_action` API.
630    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
631    /// borrow, so those mutators are inert there.)
632    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
633    where
634        I: CharStream,
635    {
636        let _ = (ctx, action);
637        false
638    }
639
640    /// Runs after runtime-owned lexer state has been reset for reuse.
641    ///
642    /// Implementations should clear extension-owned transient state here.
643    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
644    where
645        I: CharStream,
646    {
647        let _ = ctx;
648    }
649
650    /// Runs before the runtime returns a queued token or starts a new ATN
651    /// token match.
652    ///
653    /// The callback also runs between internal `skip`/`more` matches, so it
654    /// observes every point where another ATN match may start.
655    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
656    where
657        I: CharStream,
658    {
659        let _ = ctx;
660    }
661
662    /// Runs after the accepted path's portable and custom actions, but before
663    /// the token span is finalized and emitted.
664    ///
665    /// Accepted paths that selected `skip` or `more` are included, and the hook
666    /// may observe or override that pending token type.
667    ///
668    /// This callback has no synthetic ATN coordinate. It therefore also runs
669    /// for accepted rules that contain no action or predicate.
670    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
671    where
672        I: CharStream,
673    {
674        let _ = ctx;
675    }
676
677    /// Observes a token after committed lexer actions and portable commands
678    /// have run and the token has been emitted, immediately before it is
679    /// returned to the token stream.
680    ///
681    /// Hidden and custom-channel tokens are included. `skip` and intermediate
682    /// `more` matches do not produce callbacks.
683    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
684        let _ = token;
685    }
686}
687
688/// Default hook object used by parsers that do not need user-supplied
689/// semantics.
690#[derive(Clone, Copy, Debug, Default)]
691pub struct NoSemanticHooks;
692
693impl SemanticHooks for NoSemanticHooks {
694    const ENABLES_LEXER_LIFECYCLE: bool = false;
695
696    fn observes_parser_predicates(&self) -> bool {
697        false
698    }
699}
700
701/// Parser semantic predicate rendered from a supported target template.
702///
703/// The metadata recognizer evaluates these at the token-stream index where the
704/// predicate transition is reached. Unsupported or absent predicate templates
705/// remain unconditional so existing generated parsers keep their previous
706/// behavior unless the generator opts into this table.
707#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
708pub enum ParserPredicate {
709    True,
710    False,
711    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
712    FalseWithMessage {
713        message: &'static str,
714    },
715    /// Target-template test helper that reports predicate evaluation before
716    /// returning the wrapped boolean value.
717    Invoke {
718        value: bool,
719    },
720    LookaheadTextEquals {
721        offset: isize,
722        text: &'static str,
723    },
724    LookaheadNotEquals {
725        offset: isize,
726        token_type: i32,
727    },
728    /// Checks that the last two consumed visible tokens were adjacent in the
729    /// token stream. Used by C# parser predicates for split operator tokens.
730    TokenPairAdjacent,
731    /// Checks a generated parser context child by rule index and text.
732    ///
733    /// If the child is absent the predicate succeeds, matching target helpers
734    /// that treat incomplete or non-matching contexts as non-restrictive.
735    ContextChildRuleTextNotEquals {
736        rule_index: usize,
737        text: &'static str,
738    },
739    /// Compares the current rule invocation's integer argument with a literal
740    /// value from a supported `ValEquals("$i", "...")` target template.
741    LocalIntEquals {
742        value: i64,
743    },
744    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
745    /// rule invocation's integer argument.
746    LocalIntLessOrEqual {
747        value: i64,
748    },
749    /// Compares a generated parser integer member modulo a literal value.
750    MemberModuloEquals {
751        member: usize,
752        modulus: i64,
753        value: i64,
754        equals: bool,
755    },
756    /// Compares a generated parser integer member with a literal value.
757    MemberEquals {
758        member: usize,
759        value: i64,
760        equals: bool,
761    },
762}
763
764impl ParserPredicate {
765    /// Lowers the legacy predicate metadata variant into `SemIR`.
766    ///
767    /// This is the compatibility adapter for generated parsers produced while
768    /// the runtime still emitted closed enum tables. Newer generated parsers
769    /// emit `SemIR` directly.
770    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
771        match self {
772            Self::True => ir.expr(PExpr::Bool(true)),
773            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
774            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
775            Self::LookaheadTextEquals { offset, text } => {
776                let token = ir.expr(PExpr::TokenText(offset));
777                let text = ir.intern(text);
778                let text = ir.expr(PExpr::Str(text));
779                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
780            }
781            Self::LookaheadNotEquals { offset, token_type } => {
782                let actual = ir.expr(PExpr::La(offset));
783                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
784                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
785            }
786            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
787            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
788                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
789                let expected = ir.intern(text);
790                let expected = ir.expr(PExpr::Str(expected));
791                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
792            }
793            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
794            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
795            Self::MemberModuloEquals {
796                member,
797                modulus,
798                value,
799                equals,
800            } => {
801                if modulus == 0 {
802                    return ir.expr(PExpr::Bool(false));
803                }
804                let member = ir.expr(PExpr::Member(member));
805                let modulus = ir.expr(PExpr::Int(modulus));
806                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
807                let expected = ir.expr(PExpr::Int(value));
808                ir.expr(PExpr::Cmp(
809                    if equals { CmpOp::Eq } else { CmpOp::Ne },
810                    actual,
811                    expected,
812                ))
813            }
814            Self::MemberEquals {
815                member,
816                value,
817                equals,
818            } => {
819                let actual = ir.expr(PExpr::Member(member));
820                let expected = ir.expr(PExpr::Int(value));
821                ir.expr(PExpr::Cmp(
822                    if equals { CmpOp::Eq } else { CmpOp::Ne },
823                    actual,
824                    expected,
825                ))
826            }
827        }
828    }
829
830    #[must_use]
831    pub const fn failure_message(self) -> Option<&'static str> {
832        match self {
833            Self::FalseWithMessage { message } => Some(message),
834            Self::True
835            | Self::False
836            | Self::Invoke { .. }
837            | Self::LookaheadTextEquals { .. }
838            | Self::LookaheadNotEquals { .. }
839            | Self::TokenPairAdjacent
840            | Self::ContextChildRuleTextNotEquals { .. }
841            | Self::LocalIntEquals { .. }
842            | Self::LocalIntLessOrEqual { .. }
843            | Self::MemberModuloEquals { .. }
844            | Self::MemberEquals { .. } => None,
845        }
846    }
847}
848
849fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
850    let local = ir.expr(PExpr::LocalArg);
851    let absent = ir.expr(PExpr::IsNull(local));
852    let expected = ir.expr(PExpr::Int(value));
853    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
854    ir.expr(PExpr::Or([absent, comparison].into()))
855}
856
857/// Policy for semantic predicate coordinates that have no runtime
858/// implementation.
859///
860/// ANTLR grammars may embed target-language predicates that the metadata
861/// generator could not translate into a [`ParserPredicate`] table entry. When
862/// recognition reaches such a coordinate the runtime cannot know the grammar
863/// author's intent, so the caller chooses how to proceed.
864///
865/// The default is [`Self::AssumeTrue`], matching the historical behavior of
866/// this runtime. That default is deprecated and will change to [`Self::Error`]
867/// in a future minor release; grammars relying on unconditional predicates
868/// should opt in explicitly.
869#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
870pub enum UnknownSemanticPolicy {
871    /// Treat the predicate as passing, as if it were absent from the grammar.
872    #[default]
873    AssumeTrue,
874    /// Treat the predicate as failing, removing the guarded alternative.
875    AssumeFalse,
876    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
877    /// coordinate that recognition evaluated.
878    Error,
879}
880
881/// Resolves a predicate coordinate that neither a translated table entry nor a
882/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
883///
884/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
885/// so the parse entry can surface every unresolved coordinate afterwards. Both
886/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
887/// funnel through here so a missing implementation is never silently coerced
888/// to a boolean (design goal G1: never silently mis-parse).
889fn apply_unknown_predicate_policy(
890    policy: UnknownSemanticPolicy,
891    rule_index: usize,
892    pred_index: usize,
893    hits: &mut Vec<(usize, usize)>,
894) -> bool {
895    match policy {
896        UnknownSemanticPolicy::AssumeTrue => true,
897        UnknownSemanticPolicy::AssumeFalse => false,
898        UnknownSemanticPolicy::Error => {
899            let coordinate = (rule_index, pred_index);
900            if !hits.contains(&coordinate) {
901                hits.push(coordinate);
902            }
903            false
904        }
905    }
906}
907
908/// Interval-set of expected token types, displayable through a vocabulary —
909/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
910/// generated test actions.
911#[derive(Clone, Debug, Eq, PartialEq)]
912pub struct ExpectedTokenSet {
913    symbols: BTreeSet<i32>,
914}
915
916impl ExpectedTokenSet {
917    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
918    #[must_use]
919    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
920        expected_symbols_display(&self.symbols, vocabulary)
921    }
922}
923
924/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
925///
926/// The first syntax error aborts the parse instead of recovering. Generated
927/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
928#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
929pub struct BailErrorStrategy;
930
931impl BailErrorStrategy {
932    #[must_use]
933    pub const fn new() -> Self {
934        Self
935    }
936}
937
938/// Prediction strategy requested by generated parser harnesses.
939#[derive(Clone, Copy, Debug, Eq, PartialEq)]
940pub enum PredictionMode {
941    /// Prefer the clean full-context outcome when alternatives reach the same
942    /// input position.
943    Ll,
944    /// Preserve SLL's first-viable alternative bias at a decision, even when a
945    /// later full-context alternative could avoid recovery.
946    Sll,
947    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
948    LlExactAmbigDetection,
949}
950
951/// Integer argument metadata for a generated parser rule invocation.
952///
953/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
954/// the generator records the rule-transition source state and the value that
955/// should be visible to semantic predicates inside the callee.
956#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
957pub struct ParserRuleArg {
958    /// ATN state containing the rule transition that receives this argument.
959    pub source_state: usize,
960    /// Callee rule index for the transition.
961    pub rule_index: usize,
962    /// Literal fallback value to expose in the callee.
963    pub value: i64,
964    /// Whether the callee should inherit the caller's current integer argument.
965    pub inherit_local: bool,
966}
967
968/// Integer member mutation attached to an ATN action transition.
969#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
970pub struct ParserMemberAction {
971    /// ATN state containing the action transition.
972    pub source_state: usize,
973    /// Generator-assigned integer member id.
974    pub member: usize,
975    /// Delta applied when the action is reached on one speculative path.
976    pub delta: i64,
977}
978
979/// Integer return-value assignment attached to an ATN action transition.
980///
981/// Generated parsers use this metadata when target actions assign a simple
982/// return field such as `$y=1000;`. The interpreter applies it while selecting
983/// the recognized path so the finished parse tree can answer later
984/// `$label.y` action templates.
985#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
986pub struct ParserReturnAction {
987    /// ATN state containing the action transition.
988    pub source_state: usize,
989    /// Rule index recorded by the serialized action transition.
990    pub rule_index: usize,
991    /// Return-field name as it appears in the grammar.
992    pub name: &'static str,
993    /// Literal integer value assigned by the action.
994    pub value: i64,
995}
996
997impl ParserMemberAction {
998    /// Lowers this speculative member mutation into a `SemIR` action.
999    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1000        let delta = ir.expr(PExpr::Int(self.delta));
1001        ParserSemanticAction {
1002            source_state: self.source_state,
1003            rule_index: usize::MAX,
1004            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
1005            speculative: true,
1006        }
1007    }
1008}
1009
1010impl ParserReturnAction {
1011    /// Lowers this committed return-value assignment into a `SemIR` action.
1012    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1013        let name = ir.intern(self.name);
1014        let value = ir.expr(PExpr::Int(self.value));
1015        ParserSemanticAction {
1016            source_state: self.source_state,
1017            rule_index: self.rule_index,
1018            stmt: ir.stmt(AStmt::SetReturn(name, value)),
1019            speculative: false,
1020        }
1021    }
1022}
1023
1024/// Parser predicate coordinate lowered into [`SemIr`].
1025#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1026pub struct ParserSemanticPredicate {
1027    /// Serialized rule index that owns this predicate.
1028    pub rule_index: usize,
1029    /// Predicate index inside the owning rule.
1030    pub pred_index: usize,
1031    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1032    pub expr: ExprId,
1033    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1034    pub failure_message: Option<&'static str>,
1035}
1036
1037/// Parser action coordinate lowered into [`SemIr`].
1038#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1039pub struct ParserSemanticAction {
1040    /// ATN state containing the action transition.
1041    pub source_state: usize,
1042    /// Serialized rule index recorded by the action transition.
1043    pub rule_index: usize,
1044    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1045    pub stmt: StmtId,
1046    /// Whether this action may run on speculative recognition paths.
1047    pub speculative: bool,
1048}
1049
1050/// Data-driven semantic tables emitted by generated parsers.
1051///
1052/// This is the runtime representation for issue #9's `SemIR` path. Existing
1053/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1054/// remain accepted as deprecated adapters for generated code produced before
1055/// this table existed.
1056#[derive(Clone, Debug, Default, Eq, PartialEq)]
1057pub struct ParserSemantics {
1058    pub ir: SemIr,
1059    pub predicates: Vec<ParserSemanticPredicate>,
1060    pub actions: Vec<ParserSemanticAction>,
1061}
1062
1063/// Optional generated-runtime metadata for metadata-driven parser execution.
1064#[derive(Clone, Copy, Debug, Default)]
1065pub struct ParserRuntimeOptions<'a> {
1066    /// Rule indexes whose `@init` actions should be replayed.
1067    pub init_action_rules: &'a [usize],
1068    /// Whether generated parse-tree contexts should retain alternative numbers.
1069    pub track_alt_numbers: bool,
1070    /// Whether generated typed contexts should retain private dispatch alternatives.
1071    ///
1072    /// Unlike `track_alt_numbers`, this metadata does not affect the public
1073    /// alternative number or parse-tree rendering.
1074    #[doc(hidden)]
1075    pub track_context_alt_numbers: bool,
1076    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1077    pub predicates: &'a [(usize, usize, ParserPredicate)],
1078    /// `SemIR` predicate/action table emitted by newer generated parsers.
1079    pub semantics: Option<&'a ParserSemantics>,
1080    /// Rule-call integer argument table keyed by ATN source state.
1081    pub rule_args: &'a [ParserRuleArg],
1082    /// Integer member mutations keyed by ATN action source state.
1083    pub member_actions: &'a [ParserMemberAction],
1084    /// Integer return assignments keyed by ATN action source state.
1085    pub return_actions: &'a [ParserReturnAction],
1086    /// How to evaluate semantic predicate coordinates absent from
1087    /// `predicates`.
1088    pub unknown_predicate_policy: UnknownSemanticPolicy,
1089}
1090
1091pub trait Parser: Recognizer {
1092    /// Reports whether generated parser rules should build parse-tree nodes
1093    /// while recognizing input.
1094    fn build_parse_trees(&self) -> bool;
1095
1096    /// Enables or disables parse-tree construction for subsequent rule calls.
1097    fn set_build_parse_trees(&mut self, build: bool);
1098
1099    /// Returns the number of parser syntax errors recorded by committed parse
1100    /// paths so far.
1101    fn number_of_syntax_errors(&self) -> usize {
1102        0
1103    }
1104
1105    /// Reports whether prediction diagnostic-listener messages are emitted
1106    /// during parser ATN recognition.
1107    fn report_diagnostic_errors(&self) -> bool {
1108        false
1109    }
1110
1111    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1112    /// rule calls.
1113    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1114
1115    /// Reports the prediction strategy used when selecting among alternatives.
1116    fn prediction_mode(&self) -> PredictionMode {
1117        PredictionMode::Ll
1118    }
1119
1120    /// Sets the prediction strategy for subsequent rule calls.
1121    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1122}
1123
1124#[derive(Debug)]
1125struct LeftRecursiveCallerOverlap {
1126    atn_key: SharedAtnCacheKey,
1127    state_number: usize,
1128    symbol: i32,
1129    context_version: usize,
1130    overlaps: bool,
1131}
1132
1133const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1134
1135#[derive(Debug)]
1136pub struct BaseParser<S, H = NoSemanticHooks> {
1137    input: CommonTokenStream<S>,
1138    tree: ParseTreeStorage,
1139    data: RecognizerData,
1140    semantic_hooks: H,
1141    decision_override_generation: usize,
1142    build_parse_trees: bool,
1143    syntax_errors: usize,
1144    report_diagnostic_errors: bool,
1145    prediction_mode: PredictionMode,
1146    prediction_diagnostics: Vec<ParserDiagnostic>,
1147    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1148    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1149    generated_sync_expected: Option<TokenBitSet>,
1150    generated_recovery_error_index: Option<usize>,
1151    generated_recovery_error_states: BTreeSet<isize>,
1152    int_members: BTreeMap<usize, i64>,
1153    rule_context_stack: Vec<RuleContextFrame>,
1154    rule_context_version: usize,
1155    left_recursive_caller_overlap_cache:
1156        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1157    pending_invoking_states: Vec<isize>,
1158    precedence_stack: Vec<i32>,
1159    /// Predicate side effects are observable in a few target-template tests;
1160    /// speculative recognition may revisit the same coordinate, so replay it
1161    /// once per parser instance.
1162    invoked_predicates: Vec<(usize, usize)>,
1163    /// Bail error strategy: the first syntax error aborts the parse instead of
1164    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1165    /// through `set_error_handler(BailErrorStrategy::new())`.
1166    bail_on_error: bool,
1167    /// How to evaluate predicate coordinates missing from the active
1168    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1169    unknown_predicate_policy: UnknownSemanticPolicy,
1170    /// Unknown predicate coordinates evaluated by the current parse, recorded
1171    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1172    unknown_predicate_hits: Vec<(usize, usize)>,
1173    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1174    /// that no hook handled, recorded so a generated `hook`/error-disposed
1175    /// action fails loud instead of being silently dropped. Keyed by
1176    /// `(rule_index, source_state)`.
1177    unhandled_action_hits: Vec<(usize, usize)>,
1178    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1179    /// hot rule-transition checks to a vector lookup after the first visit
1180    /// while the thread-local shared ATN cache still owns the cross-parse
1181    /// computed value.
1182    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1183    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1184    /// epsilon-reachable consuming transition and shows up as a hot loop in
1185    /// `next_recovery_context` and recovery diagnostics on long inputs.
1186    /// Keying on `state_number` and sharing the result through `Rc` removes
1187    /// repeated DFS plus per-call `BTreeSet` allocations.
1188    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1189    /// Same expected-symbol cache as a bitset for generated parser sync.
1190    /// Successful parses only need `contains` and union; keeping that path out
1191    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1192    /// check and defers deterministic formatting to diagnostics.
1193    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1194    /// Per-state cache for whether a return state can finish its owning rule
1195    /// without consuming more input. Generated-parser sync uses this to walk
1196    /// parent prediction contexts for nullable exits without paying repeated
1197    /// epsilon-closure searches on every loop or optional decision.
1198    rule_stop_reach_cache: Vec<Option<bool>>,
1199    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1200    /// threads the same epsilon-recovery context through hundreds of follow
1201    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1202    /// reference bump and lets the memo key hash by pointer.
1203    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1204    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1205    /// touch a given decision state still pay no upfront cost; once cached,
1206    /// the recognizer prunes alternatives whose look-1 cannot accept the
1207    /// current lookahead, letting common SLL decisions reduce to a single
1208    /// transition walk instead of a full speculative fan-out.
1209    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1210    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1211    /// Each multi-trans visit asks "given this decision state and this
1212    /// lookahead token, which alt do I commit to?" Hitting this cache
1213    /// turns the question into a hashmap probe instead of re-scanning
1214    /// the decision's per-transition FIRST sets every visit.
1215    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1216    /// Predicate results shared by the fast recognizer's clean and recovery
1217    /// attempts. The eligible fast path keeps every runtime-provided input
1218    /// fixed, and custom predicate hooks are required to be replay-safe.
1219    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1220    /// Cache for whether an ATN state can reach itself without consuming
1221    /// input. Only those states need the recursive recognizer's
1222    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1223    /// grammar-static cache survive parser resets without reusing state
1224    /// coordinates after the parser is driven against a different ATN.
1225    empty_cycle_cache: Vec<Option<bool>>,
1226    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1227    /// Probe state for deciding whether clean-pass memo entries are worth
1228    /// storing for the current parse.
1229    clean_memo_mode: CleanMemoMode,
1230    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1231    clean_memo_probe_samples: usize,
1232    clean_memo_probe_repeats: usize,
1233    clean_memo_sparse_samples: usize,
1234    /// Reusable cycle and memo storage for one top-level fast recognition.
1235    fast_recognize_scratch: FastRecognizeTopScratch,
1236    /// Reusable direct-index/hash storage for clean speculative endpoints.
1237    fast_outcome_dedup: FastOutcomeDedupScratch,
1238    /// Empty recovery-symbols singleton used as the default at rule entry and
1239    /// after token consumption.
1240    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1241    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1242    /// prefilter trims speculative rule calls whose called rule cannot
1243    /// match the current lookahead, but it also bypasses single-token
1244    /// insertion / deletion recovery that ANTLR runs at the rule's first
1245    /// consuming transition. `parse_atn_rule` flips this off and retries
1246    /// when the first pass produces no clean outcome so the runtime can
1247    /// repair inputs the reference parser would have repaired.
1248    fast_first_set_prefilter: bool,
1249    /// Whether the fast recognizer should explore parser error-recovery paths.
1250    /// Public rule parsing starts with this disabled for the common valid-input
1251    /// path and enables it only for the retry that needs ANTLR-style repairs.
1252    fast_recovery_enabled: bool,
1253    /// Whether the fast recognizer should record terminal-token nodes while
1254    /// speculating. Clean valid-input parsing can reconstruct terminals from
1255    /// selected rule spans after recognition, avoiding many speculative
1256    /// nodes that are thrown away with losing paths.
1257    fast_token_nodes_enabled: bool,
1258    /// Whether fast recognition should retain private/public rule alternatives
1259    /// in deferred tree metadata.
1260    fast_track_alt_numbers: bool,
1261    /// Parser-owned append-only storage for speculative recognition output.
1262    /// Each public interpreted-rule entry clears lengths while retaining
1263    /// bounded backing capacities for parser reuse.
1264    recognition_arena: RecognitionArena,
1265    last_recognition_arena_root: NodeSeqId,
1266    last_recognition_arena_diagnostics: DiagnosticSeqId,
1267}
1268
1269/// Rollback marker for speculative generated parser paths.
1270#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1271pub struct GeneratedDiagnosticsCheckpoint {
1272    diagnostics_len: usize,
1273    syntax_errors: usize,
1274    tree: ParseTreeCheckpoint,
1275}
1276
1277/// Storage and reachability counters for the most recent interpreted-rule
1278/// recognition arena.
1279#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1280pub struct RecognitionArenaStats {
1281    pub total_nodes: usize,
1282    pub live_nodes: usize,
1283    pub dead_nodes: usize,
1284    pub node_capacity: usize,
1285    pub total_links: usize,
1286    pub live_links: usize,
1287    pub dead_links: usize,
1288    pub link_capacity: usize,
1289    pub total_extras: usize,
1290    pub live_extras: usize,
1291    pub dead_extras: usize,
1292    pub extra_capacity: usize,
1293}
1294
1295#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1296struct RuleContextFrame {
1297    rule_index: usize,
1298    invoking_state: isize,
1299}
1300
1301#[derive(Clone, Debug, Eq, PartialEq)]
1302struct RecognizeOutcome {
1303    index: usize,
1304    consumed_eof: bool,
1305    alt_number: usize,
1306    member_values: BTreeMap<usize, i64>,
1307    return_values: BTreeMap<String, i64>,
1308    diagnostics: DiagnosticSeqId,
1309    decisions: Vec<usize>,
1310    actions: Vec<ParserAction>,
1311    nodes: NodeSeqId,
1312}
1313
1314#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1315struct FastRecognizeOutcome {
1316    index: usize,
1317    consumed_eof: bool,
1318    diagnostics: DiagnosticSeqId,
1319    deferred_nodes: FastDeferredNodeId,
1320    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1321    /// Copying an outcome copies this compact ID; prepending appends one
1322    /// `SeqLink` without allocating an individual node or list tail.
1323    nodes: NodeSeqId,
1324}
1325
1326#[derive(Debug, Default)]
1327struct FastRecognizeTopScratch {
1328    visiting: FxHashSet<FastRecognizeKey>,
1329    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1330}
1331
1332impl FastRecognizeTopScratch {
1333    fn prepare(&mut self, memo_capacity: usize) {
1334        self.visiting.clear();
1335        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1336        self.memo.clear();
1337        self.memo.reserve(memo_capacity);
1338    }
1339
1340    fn release_oversized_memo(&mut self) {
1341        self.memo.clear();
1342        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1343            self.memo = FxHashMap::default();
1344        }
1345    }
1346}
1347
1348fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1349    buffered_tokens.saturating_mul(8).clamp(
1350        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1351        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1352    )
1353}
1354
1355#[derive(Debug, Default)]
1356struct FastOutcomeDedupScratch {
1357    dense_words: Vec<u64>,
1358    touched_dense_words: Vec<u32>,
1359    sparse_keys: FxHashSet<(usize, bool)>,
1360}
1361
1362/// Handle into the parser-owned deferred tree rope.
1363///
1364/// The sentinel keeps outcomes and repetition paths compact without an
1365/// `Option` discriminant or per-node reference counting.
1366#[repr(transparent)]
1367#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1368struct FastDeferredNodeId(u32);
1369
1370impl FastDeferredNodeId {
1371    const EMPTY: Self = Self(u32::MAX);
1372
1373    const fn is_empty(self) -> bool {
1374        self.0 == Self::EMPTY.0
1375    }
1376}
1377
1378impl Default for FastDeferredNodeId {
1379    fn default() -> Self {
1380        Self::EMPTY
1381    }
1382}
1383
1384#[repr(transparent)]
1385#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1386struct FastDeferredRuleId(u32);
1387
1388/// One immutable deferred-tree rope record in `RecognitionArena`.
1389#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1390enum FastDeferredNode {
1391    Fragment(NodeSeqId),
1392    Rule(FastDeferredRuleId),
1393    Alternative(u32),
1394    LeftRecursiveBoundary {
1395        rule_index: u32,
1396    },
1397    Concat {
1398        prefix: FastDeferredNodeId,
1399        suffix: FastDeferredNodeId,
1400    },
1401}
1402
1403#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1404struct FastDeferredRule {
1405    rule_index: u32,
1406    invoking_state: i32,
1407    start_index: u32,
1408    stop_index: Option<u32>,
1409    deferred_children: FastDeferredNodeId,
1410    children: NodeSeqId,
1411}
1412
1413#[repr(transparent)]
1414#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1415struct RecognizedNodeId(u32);
1416
1417#[repr(transparent)]
1418#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1419struct NodeSeqId(u32);
1420
1421impl NodeSeqId {
1422    const EMPTY: Self = Self(u32::MAX);
1423
1424    const fn is_empty(self) -> bool {
1425        self.0 == Self::EMPTY.0
1426    }
1427}
1428
1429impl Default for NodeSeqId {
1430    fn default() -> Self {
1431        Self::EMPTY
1432    }
1433}
1434
1435#[repr(transparent)]
1436#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1437struct DiagnosticSeqId(u32);
1438
1439impl DiagnosticSeqId {
1440    const EMPTY: Self = Self(u32::MAX);
1441
1442    const fn is_empty(self) -> bool {
1443        self.0 == Self::EMPTY.0
1444    }
1445}
1446
1447impl Default for DiagnosticSeqId {
1448    fn default() -> Self {
1449        Self::EMPTY
1450    }
1451}
1452
1453#[repr(transparent)]
1454#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1455struct RecognitionExtraId(u32);
1456
1457#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1458struct SeqLink {
1459    head: RecognizedNodeId,
1460    tail: NodeSeqId,
1461}
1462
1463#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1464struct DiagnosticLink {
1465    head: RecognitionExtraId,
1466    tail: DiagnosticSeqId,
1467}
1468
1469struct ArenaRuleSpec {
1470    rule_index: usize,
1471    invoking_state: isize,
1472    alt_number: usize,
1473    start_index: usize,
1474    stop_index: Option<usize>,
1475    return_values: BTreeMap<String, i64>,
1476    children: NodeSeqId,
1477}
1478
1479/// Compact speculative node record. Common records contain only IDs and
1480/// scalars; missing-token text and generated return values live in `extras`.
1481#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1482enum ArenaRecognizedNode {
1483    Token {
1484        token: TokenId,
1485    },
1486    ErrorToken {
1487        token: TokenId,
1488    },
1489    MissingToken {
1490        extra: RecognitionExtraId,
1491    },
1492    Rule {
1493        rule_index: u32,
1494        invoking_state: i32,
1495        alt_number: u32,
1496        start_index: u32,
1497        stop_index: Option<u32>,
1498        return_values: Option<RecognitionExtraId>,
1499        children: NodeSeqId,
1500    },
1501    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1502    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1503    /// public rule entry hands the tree to the caller.
1504    LeftRecursiveBoundary {
1505        rule_index: u32,
1506        alt_number: u32,
1507    },
1508}
1509
1510#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1511enum RecognitionExtra {
1512    MissingToken {
1513        token_type: i32,
1514        at_index: u32,
1515        text: String,
1516    },
1517    ReturnValues(BTreeMap<String, i64>),
1518    Diagnostic(ParserDiagnostic),
1519}
1520
1521#[derive(Debug, Default)]
1522struct RecognitionArena {
1523    nodes: Vec<ArenaRecognizedNode>,
1524    seq_links: Vec<SeqLink>,
1525    diagnostic_links: Vec<DiagnosticLink>,
1526    extras: Vec<RecognitionExtra>,
1527    deferred_nodes: Vec<FastDeferredNode>,
1528    deferred_rules: Vec<FastDeferredRule>,
1529}
1530
1531// Preserve normal parser reuse while preventing one pathological parse from
1532// pinning an arbitrarily large arena for the parser's remaining lifetime.
1533const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1534const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1535const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1536const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1537const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1538const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1539
1540impl RecognitionArena {
1541    fn reset(&mut self) {
1542        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1543        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1544        reset_arena_vec(
1545            &mut self.diagnostic_links,
1546            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1547        );
1548        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1549        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1550        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1551    }
1552
1553    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1554        let id = RecognizedNodeId(
1555            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1556        );
1557        self.nodes.push(node);
1558        id
1559    }
1560
1561    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1562        let id = RecognitionExtraId(
1563            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1564        );
1565        self.extras.push(extra);
1566        id
1567    }
1568
1569    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1570        let id = NodeSeqId(
1571            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1572        );
1573        self.seq_links.push(SeqLink { head, tail });
1574        id
1575    }
1576
1577    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1578        let id = FastDeferredNodeId(
1579            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1580        );
1581        self.deferred_nodes.push(node);
1582        id
1583    }
1584
1585    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1586        let id = FastDeferredRuleId(
1587            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1588        );
1589        self.deferred_rules.push(rule);
1590        id
1591    }
1592
1593    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1594        if nodes.is_empty() {
1595            FastDeferredNodeId::EMPTY
1596        } else {
1597            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1598        }
1599    }
1600
1601    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1602        let rule = self.push_deferred_rule(rule);
1603        self.push_deferred_node(FastDeferredNode::Rule(rule))
1604    }
1605
1606    fn deferred_alternative(&mut self, alt_number: usize) -> FastDeferredNodeId {
1607        self.push_deferred_node(FastDeferredNode::Alternative(
1608            u32::try_from(alt_number).expect("alternative number fits in u32"),
1609        ))
1610    }
1611
1612    fn deferred_left_recursive_boundary(&mut self, rule_index: usize) -> FastDeferredNodeId {
1613        self.push_deferred_node(FastDeferredNode::LeftRecursiveBoundary {
1614            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
1615        })
1616    }
1617
1618    fn concat_deferred_nodes(
1619        &mut self,
1620        prefix: FastDeferredNodeId,
1621        suffix: FastDeferredNodeId,
1622    ) -> FastDeferredNodeId {
1623        if prefix.is_empty() {
1624            return suffix;
1625        }
1626        if suffix.is_empty() {
1627            return prefix;
1628        }
1629        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1630    }
1631
1632    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1633        self.deferred_nodes[id.0 as usize]
1634    }
1635
1636    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1637        self.deferred_rules[id.0 as usize]
1638    }
1639
1640    fn prepend_diagnostic(
1641        &mut self,
1642        tail: DiagnosticSeqId,
1643        diagnostic: ParserDiagnostic,
1644    ) -> DiagnosticSeqId {
1645        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1646        self.prepend_diagnostic_id(tail, head)
1647    }
1648
1649    fn prepend_diagnostic_id(
1650        &mut self,
1651        tail: DiagnosticSeqId,
1652        head: RecognitionExtraId,
1653    ) -> DiagnosticSeqId {
1654        let id = DiagnosticSeqId(
1655            u32::try_from(self.diagnostic_links.len())
1656                .expect("diagnostic sequence arena fits in u32"),
1657        );
1658        self.diagnostic_links.push(DiagnosticLink { head, tail });
1659        id
1660    }
1661
1662    fn concat_diagnostics(
1663        &mut self,
1664        prefix: DiagnosticSeqId,
1665        mut suffix: DiagnosticSeqId,
1666    ) -> DiagnosticSeqId {
1667        if prefix.is_empty() {
1668            return suffix;
1669        }
1670        if suffix.is_empty() {
1671            return prefix;
1672        }
1673        let mut reversed = DiagnosticSeqId::EMPTY;
1674        let mut cursor = prefix;
1675        while let Some(link) = self.diagnostic_link(cursor) {
1676            reversed = self.prepend_diagnostic_id(reversed, link.head);
1677            cursor = link.tail;
1678        }
1679        while let Some(link) = self.diagnostic_link(reversed) {
1680            suffix = self.prepend_diagnostic_id(suffix, link.head);
1681            reversed = link.tail;
1682        }
1683        suffix
1684    }
1685
1686    #[cfg(test)]
1687    fn diagnostic_sequence(
1688        &mut self,
1689        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1690    ) -> DiagnosticSeqId {
1691        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1692        let mut sequence = DiagnosticSeqId::EMPTY;
1693        for diagnostic in diagnostics.into_iter().rev() {
1694            sequence = self.prepend_diagnostic(sequence, diagnostic);
1695        }
1696        sequence
1697    }
1698
1699    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1700        self.nodes[id.0 as usize]
1701    }
1702
1703    fn set_boundary_alt_number(&mut self, id: RecognizedNodeId, alt_number: u32) {
1704        let ArenaRecognizedNode::LeftRecursiveBoundary {
1705            alt_number: stored, ..
1706        } = &mut self.nodes[id.0 as usize]
1707        else {
1708            unreachable!("deferred boundary must materialize as a boundary node");
1709        };
1710        *stored = alt_number;
1711    }
1712
1713    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1714        &self.extras[id.0 as usize]
1715    }
1716
1717    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1718        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1719    }
1720
1721    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1722        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1723    }
1724
1725    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1726        NodeSeqIter {
1727            arena: self,
1728            cursor: sequence,
1729        }
1730    }
1731
1732    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1733        DiagnosticSeqIter {
1734            arena: self,
1735            cursor: sequence,
1736        }
1737    }
1738
1739    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1740        self.diagnostics(sequence).count()
1741    }
1742
1743    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1744        self.diagnostics(sequence)
1745            .filter(|diagnostic| {
1746                diagnostic.message.starts_with("mismatched input ")
1747                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1748            })
1749            .count()
1750    }
1751
1752    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1753        self.diagnostics(left).cmp(self.diagnostics(right))
1754    }
1755
1756    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1757        self.iter(sequence).count()
1758    }
1759
1760    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1761        self.iter(sequence).any(|node| match self.node(node) {
1762            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1763            ArenaRecognizedNode::Rule { children, .. } => {
1764                self.sequence_has_left_recursive_boundary(children)
1765            }
1766            ArenaRecognizedNode::Token { .. }
1767            | ArenaRecognizedNode::ErrorToken { .. }
1768            | ArenaRecognizedNode::MissingToken { .. } => false,
1769        })
1770    }
1771
1772    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1773        self.iter(sequence).any(|node| {
1774            matches!(
1775                self.node(node),
1776                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1777            )
1778        })
1779    }
1780
1781    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1782        self.iter(sequence).any(|node| {
1783            matches!(
1784                self.node(node),
1785                ArenaRecognizedNode::Token { .. }
1786                    | ArenaRecognizedNode::ErrorToken { .. }
1787                    | ArenaRecognizedNode::MissingToken { .. }
1788            )
1789        })
1790    }
1791
1792    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1793        match self.node(node) {
1794            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1795                Some(token.index())
1796            }
1797            ArenaRecognizedNode::MissingToken { extra } => {
1798                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1799                    unreachable!("missing-token node must reference missing-token extra");
1800                };
1801                Some(*at_index as usize)
1802            }
1803            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1804            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1805        }
1806    }
1807
1808    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
1809        match self.node(node) {
1810            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1811                Some(token.index())
1812            }
1813            ArenaRecognizedNode::MissingToken { extra } => {
1814                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1815                    unreachable!("missing-token node must reference missing-token extra");
1816                };
1817                (*at_index as usize).checked_sub(1)
1818            }
1819            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
1820            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1821        }
1822    }
1823
1824    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
1825        let start = self.node_start_index(node)?;
1826        let stop = self.node_stop_index(node);
1827        Some((start, stop))
1828    }
1829
1830    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
1831        self.iter(sequence)
1832            .find_map(|node| self.node_start_index(node))
1833    }
1834
1835    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
1836        let mut stop = None;
1837        for node in self.iter(sequence) {
1838            if let Some(index) = self.node_stop_index(node) {
1839                stop = Some(index);
1840            }
1841        }
1842        stop
1843    }
1844
1845    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
1846        self.iter(sequence)
1847            .any(|node| self.node_needs_stable_tie(node))
1848    }
1849
1850    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
1851        match self.node(node) {
1852            ArenaRecognizedNode::Token { .. }
1853            | ArenaRecognizedNode::ErrorToken { .. }
1854            | ArenaRecognizedNode::MissingToken { .. } => false,
1855            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1856            ArenaRecognizedNode::Rule {
1857                rule_index,
1858                children,
1859                ..
1860            } => self.iter(children).any(|child| {
1861                matches!(
1862                    self.node(child),
1863                    ArenaRecognizedNode::Rule {
1864                        rule_index: child_rule,
1865                        ..
1866                    } if child_rule == rule_index
1867                ) || self.node_needs_stable_tie(child)
1868            }),
1869        }
1870    }
1871
1872    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
1873        loop {
1874            match (self.link(left), self.link(right)) {
1875                (Some(left_link), Some(right_link)) => {
1876                    let order = self.compare_nodes(left_link.head, right_link.head);
1877                    if order != Ordering::Equal {
1878                        return order;
1879                    }
1880                    left = left_link.tail;
1881                    right = right_link.tail;
1882                }
1883                (None, None) => return Ordering::Equal,
1884                (None, Some(_)) => return Ordering::Less,
1885                (Some(_), None) => return Ordering::Greater,
1886            }
1887        }
1888    }
1889
1890    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
1891        let left = self.node(left);
1892        let right = self.node(right);
1893        match (left, right) {
1894            (
1895                ArenaRecognizedNode::Token { token: left },
1896                ArenaRecognizedNode::Token { token: right },
1897            )
1898            | (
1899                ArenaRecognizedNode::ErrorToken { token: left },
1900                ArenaRecognizedNode::ErrorToken { token: right },
1901            ) => left.cmp(&right),
1902            (
1903                ArenaRecognizedNode::MissingToken { extra: left },
1904                ArenaRecognizedNode::MissingToken { extra: right },
1905            ) => self.extra(left).cmp(self.extra(right)),
1906            (
1907                ArenaRecognizedNode::Rule {
1908                    rule_index: left_rule,
1909                    invoking_state: left_invoking,
1910                    alt_number: left_alt,
1911                    start_index: left_start,
1912                    stop_index: left_stop,
1913                    return_values: left_returns,
1914                    children: left_children,
1915                },
1916                ArenaRecognizedNode::Rule {
1917                    rule_index: right_rule,
1918                    invoking_state: right_invoking,
1919                    alt_number: right_alt,
1920                    start_index: right_start,
1921                    stop_index: right_stop,
1922                    return_values: right_returns,
1923                    children: right_children,
1924                },
1925            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
1926                .cmp(&(
1927                    right_rule,
1928                    right_invoking,
1929                    right_alt,
1930                    right_start,
1931                    right_stop,
1932                ))
1933                .then_with(|| {
1934                    left_returns
1935                        .map(|id| self.extra(id))
1936                        .cmp(&right_returns.map(|id| self.extra(id)))
1937                })
1938                .then_with(|| self.compare_sequences(left_children, right_children)),
1939            (
1940                ArenaRecognizedNode::LeftRecursiveBoundary {
1941                    rule_index: left_rule,
1942                    alt_number: left_alt,
1943                },
1944                ArenaRecognizedNode::LeftRecursiveBoundary {
1945                    rule_index: right_rule,
1946                    alt_number: right_alt,
1947                },
1948            ) => (left_rule, left_alt).cmp(&(right_rule, right_alt)),
1949            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
1950        }
1951    }
1952
1953    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1954        let mut reversed = NodeSeqId::EMPTY;
1955        while let Some(link) = self.link(sequence) {
1956            reversed = self.prepend(reversed, link.head);
1957            sequence = link.tail;
1958        }
1959        reversed
1960    }
1961
1962    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1963        if !self.sequence_has_direct_boundary(sequence) {
1964            return sequence;
1965        }
1966        let mut reversed = NodeSeqId::EMPTY;
1967        while let Some(link) = self.link(sequence) {
1968            match self.node(link.head) {
1969                ArenaRecognizedNode::LeftRecursiveBoundary {
1970                    rule_index,
1971                    alt_number,
1972                } => {
1973                    if !reversed.is_empty() {
1974                        let children = self.reverse_sequence(reversed);
1975                        let start_index = self.sequence_start_index(children).unwrap_or_default();
1976                        let stop_index = self.sequence_stop_index(children);
1977                        let rule = self.push_node(ArenaRecognizedNode::Rule {
1978                            rule_index,
1979                            invoking_state: -1,
1980                            alt_number,
1981                            start_index: u32::try_from(start_index)
1982                                .expect("left-recursive start index fits in u32"),
1983                            stop_index: stop_index.map(|index| {
1984                                u32::try_from(index).expect("left-recursive stop index fits in u32")
1985                            }),
1986                            return_values: None,
1987                            children,
1988                        });
1989                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
1990                    }
1991                }
1992                _ => {
1993                    reversed = self.prepend(reversed, link.head);
1994                }
1995            }
1996            sequence = link.tail;
1997        }
1998        self.reverse_sequence(reversed)
1999    }
2000
2001    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
2002        let mut live_nodes = vec![false; self.nodes.len()];
2003        let mut live_links = vec![false; self.seq_links.len()];
2004        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
2005        let mut live_extras = vec![false; self.extras.len()];
2006        let mut pending = vec![root];
2007        while let Some(mut sequence) = pending.pop() {
2008            while let Some(link) = self.link(sequence) {
2009                let link_index = sequence.0 as usize;
2010                if live_links[link_index] {
2011                    break;
2012                }
2013                live_links[link_index] = true;
2014                let node_index = link.head.0 as usize;
2015                if !live_nodes[node_index] {
2016                    live_nodes[node_index] = true;
2017                    match self.node(link.head) {
2018                        ArenaRecognizedNode::MissingToken { extra } => {
2019                            live_extras[extra.0 as usize] = true;
2020                        }
2021                        ArenaRecognizedNode::Rule {
2022                            return_values,
2023                            children,
2024                            ..
2025                        } => {
2026                            if let Some(extra) = return_values {
2027                                live_extras[extra.0 as usize] = true;
2028                            }
2029                            pending.push(children);
2030                        }
2031                        ArenaRecognizedNode::Token { .. }
2032                        | ArenaRecognizedNode::ErrorToken { .. }
2033                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
2034                    }
2035                }
2036                sequence = link.tail;
2037            }
2038        }
2039        let mut diagnostics = diagnostics;
2040        while let Some(link) = self.diagnostic_link(diagnostics) {
2041            let link_index = diagnostics.0 as usize;
2042            if live_diagnostic_links[link_index] {
2043                break;
2044            }
2045            live_diagnostic_links[link_index] = true;
2046            live_extras[link.head.0 as usize] = true;
2047            diagnostics = link.tail;
2048        }
2049        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
2050        let live_link_count = live_links.into_iter().filter(|live| *live).count()
2051            + live_diagnostic_links
2052                .into_iter()
2053                .filter(|live| *live)
2054                .count();
2055        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
2056        let total_links = self.seq_links.len() + self.diagnostic_links.len();
2057        RecognitionArenaStats {
2058            total_nodes: self.nodes.len(),
2059            live_nodes: live_node_count,
2060            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
2061            node_capacity: self.nodes.capacity(),
2062            total_links,
2063            live_links: live_link_count,
2064            dead_links: total_links.saturating_sub(live_link_count),
2065            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
2066            total_extras: self.extras.len(),
2067            live_extras: live_extra_count,
2068            dead_extras: self.extras.len().saturating_sub(live_extra_count),
2069            extra_capacity: self.extras.capacity(),
2070        }
2071    }
2072}
2073
2074fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
2075    if storage.capacity() > max_retained_capacity {
2076        *storage = Vec::new();
2077    } else {
2078        storage.clear();
2079    }
2080}
2081
2082const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2083    match node {
2084        ArenaRecognizedNode::Token { .. } => 0,
2085        ArenaRecognizedNode::ErrorToken { .. } => 1,
2086        ArenaRecognizedNode::MissingToken { .. } => 2,
2087        ArenaRecognizedNode::Rule { .. } => 3,
2088        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2089    }
2090}
2091
2092struct NodeSeqIter<'a> {
2093    arena: &'a RecognitionArena,
2094    cursor: NodeSeqId,
2095}
2096
2097impl Iterator for NodeSeqIter<'_> {
2098    type Item = RecognizedNodeId;
2099
2100    fn next(&mut self) -> Option<Self::Item> {
2101        let link = self.arena.link(self.cursor)?;
2102        self.cursor = link.tail;
2103        Some(link.head)
2104    }
2105}
2106
2107struct DiagnosticSeqIter<'a> {
2108    arena: &'a RecognitionArena,
2109    cursor: DiagnosticSeqId,
2110}
2111
2112impl<'a> Iterator for DiagnosticSeqIter<'a> {
2113    type Item = &'a ParserDiagnostic;
2114
2115    fn next(&mut self) -> Option<Self::Item> {
2116        let link = self.arena.diagnostic_link(self.cursor)?;
2117        self.cursor = link.tail;
2118        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2119            unreachable!("diagnostic link must reference diagnostic extra");
2120        };
2121        Some(diagnostic)
2122    }
2123}
2124
2125#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2126struct ParserDiagnostic {
2127    line: usize,
2128    column: usize,
2129    message: String,
2130}
2131
2132#[derive(Clone, Debug, Default, Eq, PartialEq)]
2133struct ExpectedTokens {
2134    index: Option<usize>,
2135    symbols: BTreeSet<i32>,
2136    no_viable: Option<NoViableAlternative>,
2137}
2138
2139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2140struct NoViableAlternative {
2141    start_index: usize,
2142    error_index: usize,
2143}
2144
2145impl ExpectedTokens {
2146    /// Records the expected symbols for the farthest token index reached by any
2147    /// failed ATN path.
2148    fn record_transition(
2149        &mut self,
2150        index: usize,
2151        transition: ParserTransition<'_>,
2152        max_token_type: i32,
2153    ) {
2154        let symbols = transition_expected_symbols(transition, max_token_type);
2155        match self.index {
2156            Some(current) if index < current => {}
2157            Some(current) if index == current => self.symbols.extend(symbols),
2158            _ => {
2159                self.index = Some(index);
2160                self.symbols = symbols;
2161            }
2162        }
2163    }
2164
2165    /// Records an ambiguous decision that failed after consuming a shared
2166    /// prefix, which ANTLR reports as `no viable alternative`.
2167    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2168        match self.no_viable {
2169            Some(current) if error_index < current.error_index => {}
2170            _ => {
2171                self.no_viable = Some(NoViableAlternative {
2172                    start_index,
2173                    error_index,
2174                });
2175            }
2176        }
2177    }
2178}
2179
2180/// Compact token-type set for parser-internal FIRST/lookahead caches.
2181///
2182/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2183/// but the hot recognizer path mostly needs `contains` and set union over
2184/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2185/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2186#[derive(Clone, Debug, Default, Eq, PartialEq)]
2187struct TokenBitSet {
2188    words: Vec<u64>,
2189}
2190
2191impl TokenBitSet {
2192    fn insert(&mut self, symbol: i32) {
2193        let Some(slot) = token_bit_slot(symbol) else {
2194            return;
2195        };
2196        let word = slot / u64::BITS as usize;
2197        if word >= self.words.len() {
2198            self.words.resize(word + 1, 0);
2199        }
2200        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2201    }
2202
2203    fn extend_range(&mut self, start: i32, stop: i32) {
2204        let (start, stop) = if start <= stop {
2205            (start, stop)
2206        } else {
2207            (stop, start)
2208        };
2209        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2210            self.insert(TOKEN_EOF);
2211        }
2212        let positive_start = start.max(1);
2213        if positive_start > stop {
2214            return;
2215        }
2216        let Some(start_slot) = token_bit_slot(positive_start) else {
2217            return;
2218        };
2219        let Some(stop_slot) = token_bit_slot(stop) else {
2220            return;
2221        };
2222        self.extend_slot_range(start_slot, stop_slot);
2223    }
2224
2225    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2226        if start_slot > stop_slot {
2227            return;
2228        }
2229        let start_word = start_slot / u64::BITS as usize;
2230        let stop_word = stop_slot / u64::BITS as usize;
2231        if stop_word >= self.words.len() {
2232            self.words.resize(stop_word + 1, 0);
2233        }
2234        let start_offset = start_slot % u64::BITS as usize;
2235        let stop_offset = stop_slot % u64::BITS as usize;
2236        if start_word == stop_word {
2237            self.words[start_word] |=
2238                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2239            return;
2240        }
2241        self.words[start_word] |= !0_u64 << start_offset;
2242        for word in &mut self.words[(start_word + 1)..stop_word] {
2243            *word = !0_u64;
2244        }
2245        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2246    }
2247
2248    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2249        for symbol in symbols {
2250            self.insert(symbol);
2251        }
2252    }
2253
2254    fn extend_from(&mut self, other: &Self) {
2255        if other.words.len() > self.words.len() {
2256            self.words.resize(other.words.len(), 0);
2257        }
2258        for (left, right) in self.words.iter_mut().zip(&other.words) {
2259            *left |= *right;
2260        }
2261    }
2262
2263    fn contains(&self, symbol: i32) -> bool {
2264        let Some(slot) = token_bit_slot(symbol) else {
2265            return false;
2266        };
2267        let word = slot / u64::BITS as usize;
2268        self.words
2269            .get(word)
2270            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2271    }
2272
2273    fn is_empty(&self) -> bool {
2274        self.words.iter().all(|word| *word == 0)
2275    }
2276
2277    fn symbols(&self) -> impl Iterator<Item = i32> + '_ {
2278        self.words
2279            .iter()
2280            .copied()
2281            .enumerate()
2282            .flat_map(|(word_index, mut bits)| {
2283                std::iter::from_fn(move || {
2284                    while bits != 0 {
2285                        let bit = bits.trailing_zeros() as usize;
2286                        bits &= bits - 1;
2287                        if let Some(symbol) =
2288                            token_bit_symbol(word_index * u64::BITS as usize + bit)
2289                        {
2290                            return Some(symbol);
2291                        }
2292                    }
2293                    None
2294                })
2295            })
2296    }
2297
2298    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2299        target.extend(self.symbols());
2300    }
2301
2302    fn to_btree_set(&self) -> BTreeSet<i32> {
2303        let mut out = BTreeSet::new();
2304        self.extend_btree_set(&mut out);
2305        out
2306    }
2307}
2308
2309fn token_bit_slot(symbol: i32) -> Option<usize> {
2310    if symbol == TOKEN_EOF {
2311        Some(0)
2312    } else if symbol > 0 {
2313        usize::try_from(symbol).ok()
2314    } else {
2315        None
2316    }
2317}
2318
2319fn token_bit_symbol(slot: usize) -> Option<i32> {
2320    if slot == 0 {
2321        Some(TOKEN_EOF)
2322    } else {
2323        i32::try_from(slot).ok()
2324    }
2325}
2326
2327/// Converts one consuming transition into the token types that would satisfy it
2328/// for diagnostic reporting.
2329fn transition_expected_symbols(
2330    transition: ParserTransition<'_>,
2331    max_token_type: i32,
2332) -> BTreeSet<i32> {
2333    let mut symbols = BTreeSet::new();
2334    match &transition.data() {
2335        Transition::Atom { label, .. } => {
2336            symbols.insert(*label);
2337        }
2338        Transition::Range { start, stop, .. } => {
2339            symbols.extend(*start..=*stop);
2340        }
2341        Transition::Set { set, .. } => {
2342            for (start, stop) in set.ranges() {
2343                symbols.extend(start..=stop);
2344            }
2345        }
2346        Transition::NotSet { set, .. } => {
2347            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2348        }
2349        Transition::Wildcard { .. } => {
2350            symbols.extend(1..=max_token_type);
2351        }
2352        Transition::Epsilon { .. }
2353        | Transition::Rule { .. }
2354        | Transition::Predicate { .. }
2355        | Transition::Action { .. }
2356        | Transition::Precedence { .. } => {}
2357    }
2358    symbols
2359}
2360
2361fn transition_expected_token_set(
2362    transition: ParserTransition<'_>,
2363    max_token_type: i32,
2364) -> TokenBitSet {
2365    let mut symbols = TokenBitSet::default();
2366    match &transition.data() {
2367        Transition::Atom { label, .. } => {
2368            symbols.insert(*label);
2369        }
2370        Transition::Range { start, stop, .. } => {
2371            symbols.extend_range(*start, *stop);
2372        }
2373        Transition::Set { set, .. } => {
2374            for (start, stop) in set.ranges() {
2375                symbols.extend_range(start, stop);
2376            }
2377        }
2378        Transition::NotSet { set, .. } => {
2379            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2380        }
2381        Transition::Wildcard { .. } => {
2382            symbols.extend_range(1, max_token_type);
2383        }
2384        Transition::Epsilon { .. }
2385        | Transition::Rule { .. }
2386        | Transition::Predicate { .. }
2387        | Transition::Action { .. }
2388        | Transition::Precedence { .. } => {}
2389    }
2390    symbols
2391}
2392
2393/// Returns the consuming-token expectations reachable from an ATN state through
2394/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2395/// and loop exits report the same expectation set ANTLR users see.
2396fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2397    let mut symbols = BTreeSet::new();
2398    let mut stack = vec![state_number];
2399    let mut visited = BTreeSet::new();
2400    while let Some(current) = stack.pop() {
2401        if !visited.insert(current) {
2402            continue;
2403        }
2404        let Some(state) = atn.state(current) else {
2405            continue;
2406        };
2407        for transition in &state.transitions() {
2408            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2409            if transition_symbols.is_empty() {
2410                if transition.is_epsilon() {
2411                    stack.push(transition.target());
2412                }
2413            } else {
2414                symbols.extend(transition_symbols);
2415            }
2416        }
2417    }
2418    symbols
2419}
2420
2421fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2422    let mut symbols = TokenBitSet::default();
2423    let mut stack = vec![state_number];
2424    let mut visited = BTreeSet::new();
2425    while let Some(current) = stack.pop() {
2426        if !visited.insert(current) {
2427            continue;
2428        }
2429        let Some(state) = atn.state(current) else {
2430            continue;
2431        };
2432        for transition in &state.transitions() {
2433            let transition_symbols =
2434                transition_expected_token_set(transition, atn.max_token_type());
2435            if transition_symbols.is_empty() {
2436                if transition.is_epsilon() {
2437                    stack.push(transition.target());
2438                }
2439            } else {
2440                symbols.extend_from(&transition_symbols);
2441            }
2442        }
2443    }
2444    symbols
2445}
2446
2447fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2448    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2449        return false;
2450    };
2451    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2452        return false;
2453    };
2454    epsilon_reaches_state(atn, state_number, stop_state)
2455}
2456
2457fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2458    let mut stack = vec![start];
2459    let mut visited = BTreeSet::new();
2460    while let Some(current) = stack.pop() {
2461        if current == target {
2462            return true;
2463        }
2464        if !visited.insert(current) {
2465            continue;
2466        }
2467        let Some(state) = atn.state(current) else {
2468            continue;
2469        };
2470        stack.extend(
2471            state
2472                .transitions()
2473                .iter()
2474                .filter(|transition| transition.is_epsilon())
2475                .map(ParserTransition::target),
2476        );
2477    }
2478    false
2479}
2480
2481/// FIRST set for a rule entry plus whether the rule is nullable.
2482///
2483/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2484/// a consuming transition or reaches the rule's stop state. Used by the fast
2485/// recognizer to skip rule alternatives whose first-consumed token cannot
2486/// possibly match the current lookahead.
2487#[derive(Clone, Debug, Default, Eq, PartialEq)]
2488struct FirstSet {
2489    symbols: TokenBitSet,
2490    nullable: bool,
2491}
2492
2493/// Per-parser cache of FIRST sets computed during recognition. The fast path
2494/// consults this on every speculative `Transition::Rule` encounter, so the
2495/// computation must amortize across all of those calls — the FIRST set is a
2496/// pure function of the ATN, not of the input position. Cached entries are
2497/// shared via `Rc` so the recognizer never deep-copies the underlying
2498/// `BTreeSet<i32>`.
2499type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2500
2501// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2502// and decision-lookahead entries are purely functions of the grammar's
2503// ATN, so caching across parses lets repeated parsing of the same grammar
2504// (the common case for a CLI tool or language server) avoid redoing the
2505// closure work. Generated parsers hand us a `&'static Atn` whose address
2506// is stable, which is what we hash on.
2507type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2508
2509#[derive(Debug, Default)]
2510struct LeftRecursiveOperatorLookahead {
2511    /// Operator alts whose token-prefix is fully matched by this one symbol
2512    /// (then only epsilons/actions remain before the recursive RHS call).
2513    /// Safe for one-token loop-enter fast path.
2514    single_token: TokenBitSet,
2515    /// Operator alts that start with this symbol but still require more tokens
2516    /// before the operand. Must not force enter from one-token lookahead when a
2517    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2518    /// has to weigh the exit alt as well.
2519    multi_token_prefix: TokenBitSet,
2520    predicate_dependent: TokenBitSet,
2521}
2522
2523#[derive(Default)]
2524struct SharedAtnCache {
2525    first_set: FirstSetCache,
2526    decision_lookahead: DecisionLookaheadCache,
2527    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2528    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2529    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2530    rule_stop_reach: FxHashMap<usize, bool>,
2531    observable_action_transitions: Option<bool>,
2532    predicate_transitions: Option<bool>,
2533}
2534
2535thread_local! {
2536    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2537        RefCell::new(FxHashMap::default());
2538}
2539
2540/// Compound key for `SHARED_ATN_CACHES`.
2541///
2542/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2543/// pointer identifies one grammar for the program's lifetime. For the
2544/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2545/// the secondary fields below catch the pointer collision: a new grammar
2546/// would need to match all of `(states ptr, states len, max_token_type)` to
2547/// be mistaken for the dropped one. That combination changing under us
2548/// without a rebuild is implausible enough to treat as a bug; bundling them
2549/// into the key is otherwise a few extra bytes per lookup.
2550#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2551struct SharedAtnCacheKey {
2552    atn: usize,
2553    states: usize,
2554    state_count: usize,
2555    max_token_type: i32,
2556}
2557
2558impl SharedAtnCacheKey {
2559    fn for_atn(atn: &Atn) -> Self {
2560        let (states, state_count) = atn.storage_identity();
2561        Self {
2562            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2563            states,
2564            state_count,
2565            max_token_type: atn.max_token_type(),
2566        }
2567    }
2568}
2569
2570fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2571    SHARED_ATN_CACHES.with(|cell| {
2572        let key = SharedAtnCacheKey::for_atn(atn);
2573        let mut map = cell.borrow_mut();
2574        let cache = map.entry(key).or_default();
2575        f(&mut cache.first_set)
2576    })
2577}
2578
2579fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2580    SHARED_ATN_CACHES.with(|cell| {
2581        let key = SharedAtnCacheKey::for_atn(atn);
2582        let mut map = cell.borrow_mut();
2583        let cache = map.entry(key).or_default();
2584        f(cache)
2585    })
2586}
2587
2588/// Per-decision-state cached look-1 sets for each outgoing transition.
2589///
2590/// At a multi-alternative state, the recognizer would otherwise speculatively
2591/// walk every alternative even when only one can possibly accept the current
2592/// lookahead. Caching the look-1 set per transition lets us prune the
2593/// non-viable transitions before recursing — the same SLL prediction trick
2594/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2595/// filter rather than a full DFA.
2596#[derive(Debug, Default)]
2597struct DecisionLookahead {
2598    transitions: Vec<TransitionLookSet>,
2599}
2600
2601/// Look-1 information for one outgoing transition.
2602///
2603/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2604/// can reach the rule stop without consuming a token (e.g. an empty alt).
2605/// Nullable transitions cannot be pruned: they may still be the right path
2606/// when the lookahead consumes nothing further inside the current rule.
2607#[derive(Clone, Debug, Default)]
2608struct TransitionLookSet {
2609    symbols: TokenBitSet,
2610    nullable: bool,
2611}
2612
2613/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2614/// rarely-touched fields keeps the recursive helpers below the function-arity
2615/// lint and lets every nested call thread the same cache and cycle guards.
2616struct FirstSetCtx<'a> {
2617    cache: &'a mut FirstSetCache,
2618    in_progress: BTreeSet<(usize, usize)>,
2619    hit_cycle: bool,
2620}
2621
2622/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2623/// shared cache and tolerating recursive nullable rule chains. Mutually
2624/// recursive rules cannot stack-overflow because callers in flight are tracked
2625/// in `ctx.in_progress`; revisits return without recursing, and the partial
2626/// result is cached only when no cycle was detected during its computation.
2627///
2628/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2629/// rule-call probes only pay a reference bump.
2630fn rule_first_set(
2631    atn: &Atn,
2632    target: usize,
2633    rule_stop_state: usize,
2634    cache: &mut FirstSetCache,
2635) -> Rc<FirstSet> {
2636    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2637        return Rc::clone(cached);
2638    }
2639    let mut ctx = FirstSetCtx {
2640        cache,
2641        in_progress: BTreeSet::new(),
2642        hit_cycle: false,
2643    };
2644    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2645}
2646
2647fn rule_first_set_cached(
2648    atn: &Atn,
2649    target: usize,
2650    rule_stop_state: usize,
2651    ctx: &mut FirstSetCtx<'_>,
2652) -> Rc<FirstSet> {
2653    let key = (target, rule_stop_state);
2654    if let Some(cached) = ctx.cache.get(&key) {
2655        return Rc::clone(cached);
2656    }
2657    if !ctx.in_progress.insert(key) {
2658        // Cycle: a caller above is already computing this entry. Return an
2659        // empty FIRST set; that caller's traversal supplies the contributions
2660        // from the rule's other alternatives.
2661        return Rc::new(FirstSet::default());
2662    }
2663    let saved_hit_cycle = ctx.hit_cycle;
2664    ctx.hit_cycle = false;
2665    let mut first = FirstSet::default();
2666    let mut visited = BTreeSet::new();
2667    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2668    ctx.in_progress.remove(&key);
2669    let entry = Rc::new(first);
2670    if !ctx.hit_cycle {
2671        ctx.cache.insert(key, Rc::clone(&entry));
2672    }
2673    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2674    entry
2675}
2676
2677/// Returns the look-1 set for traversing `transition` while still inside the
2678/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2679/// prunes transitions whose look-1 cannot accept the current lookahead.
2680fn transition_first_set(
2681    atn: &Atn,
2682    transition: ParserTransition<'_>,
2683    rule_stop_state: usize,
2684    cache: &mut FirstSetCache,
2685) -> TransitionLookSet {
2686    match &transition.data() {
2687        Transition::Atom { label, .. } => {
2688            let mut symbols = TokenBitSet::default();
2689            symbols.insert(*label);
2690            TransitionLookSet {
2691                symbols,
2692                nullable: false,
2693            }
2694        }
2695        Transition::Range { start, stop, .. } => {
2696            let mut symbols = TokenBitSet::default();
2697            symbols.extend_range(*start, *stop);
2698            TransitionLookSet {
2699                symbols,
2700                nullable: false,
2701            }
2702        }
2703        Transition::Set { set, .. } => {
2704            let mut symbols = TokenBitSet::default();
2705            for (start, stop) in set.ranges() {
2706                symbols.extend_range(start, stop);
2707            }
2708            TransitionLookSet {
2709                symbols,
2710                nullable: false,
2711            }
2712        }
2713        Transition::NotSet { set, .. } => {
2714            let max = atn.max_token_type();
2715            let mut symbols = TokenBitSet::default();
2716            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2717            TransitionLookSet {
2718                symbols,
2719                nullable: false,
2720            }
2721        }
2722        Transition::Wildcard { .. } => {
2723            let mut symbols = TokenBitSet::default();
2724            symbols.extend_range(1, atn.max_token_type());
2725            TransitionLookSet {
2726                symbols,
2727                nullable: false,
2728            }
2729        }
2730        Transition::Epsilon { target }
2731        | Transition::Action { target, .. }
2732        | Transition::Predicate { target, .. }
2733        | Transition::Precedence { target, .. } => {
2734            // Walk the closure starting at `target` until a consuming transition
2735            // is reached or the rule stop state is hit.
2736            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2737            TransitionLookSet {
2738                symbols: first.symbols.clone(),
2739                nullable: first.nullable,
2740            }
2741        }
2742        Transition::Rule {
2743            target,
2744            rule_index,
2745            follow_state,
2746            ..
2747        } => {
2748            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2749                return TransitionLookSet::default();
2750            };
2751            let child = rule_first_set(atn, *target, child_stop, cache);
2752            let mut symbols = child.symbols.clone();
2753            let nullable = if child.nullable {
2754                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2755                symbols.extend_from(&follow.symbols);
2756                follow.nullable
2757            } else {
2758                false
2759            };
2760            TransitionLookSet { symbols, nullable }
2761        }
2762    }
2763}
2764
2765/// Reports whether `transition` can be pruned at a multi-alt state because
2766/// its cached look-1 cannot accept the current lookahead.
2767///
2768/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2769/// Rule/Precedence) so consuming transitions still reach the
2770/// `matches`+recovery path that surfaces single-token deletion / insertion
2771/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2772/// transition is pruned, its FIRST set is folded into `expected` so failed
2773/// parses produce the same `mismatched input ... expecting ...` diagnostic
2774/// the no-prefilter baseline would emit.
2775/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2776/// one transition's FIRST set and no transition is nullable. Used as an
2777/// LL(1) commit point: when prediction is unambiguous from the lookahead
2778/// alone, the recursive recognizer can skip every other alt without paying
2779/// for the per-transition filter probe.
2780///
2781/// `None` signals the caller to fall back to per-transition lookahead
2782/// filtering. Returning `Some` for an alt whose transition cannot actually
2783/// match would prune the only viable parse path; this is why we require
2784/// strict disjointness *and* no nullable transitions in the decision.
2785fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2786    let mut chosen: Option<usize> = None;
2787    for (index, transition) in entry.transitions.iter().enumerate() {
2788        if transition.nullable {
2789            return None;
2790        }
2791        if transition.symbols.contains(symbol) {
2792            if chosen.is_some() {
2793                return None;
2794            }
2795            chosen = Some(index);
2796        }
2797    }
2798    chosen
2799}
2800
2801/// Returns the unique greedy alt index (0-based) selected by the current
2802/// lookahead.
2803///
2804/// The shortcut is intentionally conservative around nullable exits. If the
2805/// current symbol can start a consuming alternative and an empty alternative is
2806/// also present, one-token lookahead is not enough to know whether the symbol
2807/// belongs to the current construct or to its caller's follow set. `None`
2808/// signals the caller to fall back to adaptive prediction.
2809fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2810    let mut matching_non_nullable_alt = None;
2811    let mut nullable_alt = None;
2812    for (index, transition) in entry.transitions.iter().enumerate() {
2813        if transition.nullable {
2814            if nullable_alt.is_some() {
2815                return None;
2816            }
2817            nullable_alt = Some(index);
2818        }
2819        if transition.symbols.contains(symbol) {
2820            if transition.nullable {
2821                continue;
2822            }
2823            if matching_non_nullable_alt.is_some() {
2824                return None;
2825            }
2826            matching_non_nullable_alt = Some(index);
2827        }
2828    }
2829    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
2830        return None;
2831    }
2832    if non_greedy {
2833        nullable_alt.or(matching_non_nullable_alt)
2834    } else {
2835        matching_non_nullable_alt.or(nullable_alt)
2836    }
2837}
2838
2839fn should_skip_via_lookahead(
2840    transition_kind: ParserTransitionKind,
2841    transition_index: usize,
2842    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
2843    index: usize,
2844    record_expected: bool,
2845    expected: &mut ExpectedTokens,
2846) -> bool {
2847    let prune_non_consuming = matches!(
2848        transition_kind,
2849        ParserTransitionKind::Epsilon
2850            | ParserTransitionKind::Action
2851            | ParserTransitionKind::Predicate
2852            | ParserTransitionKind::Rule
2853            | ParserTransitionKind::Precedence
2854    );
2855    if !prune_non_consuming {
2856        return false;
2857    }
2858    let Some((symbol, entry)) = lookahead_filter else {
2859        return false;
2860    };
2861    let Some(set) = entry.transitions.get(transition_index) else {
2862        return false;
2863    };
2864    if set.symbols.contains(*symbol) || set.nullable {
2865        return false;
2866    }
2867    if record_expected && !set.symbols.is_empty() {
2868        record_pruned_transition_expected(set, index, expected);
2869    }
2870    true
2871}
2872
2873fn should_skip_rule_via_first_set(
2874    first: &FirstSet,
2875    symbol: i32,
2876    record_expected: bool,
2877    index: usize,
2878    expected: &mut ExpectedTokens,
2879) -> bool {
2880    if first.nullable || first.symbols.contains(symbol) {
2881        return false;
2882    }
2883    if record_expected && !first.symbols.is_empty() {
2884        record_token_bit_expected(&first.symbols, index, expected);
2885    }
2886    true
2887}
2888
2889fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
2890    match expected.index {
2891        Some(current) if index < current => {}
2892        Some(current) if index == current => {
2893            symbols.extend_btree_set(&mut expected.symbols);
2894        }
2895        _ => {
2896            expected.index = Some(index);
2897            expected.symbols = symbols.to_btree_set();
2898        }
2899    }
2900}
2901
2902/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
2903fn record_pruned_transition_expected(
2904    set: &TransitionLookSet,
2905    index: usize,
2906    expected: &mut ExpectedTokens,
2907) {
2908    match expected.index {
2909        Some(current) if index < current => {}
2910        Some(current) if index == current => {
2911            set.symbols.extend_btree_set(&mut expected.symbols);
2912        }
2913        _ => {
2914            expected.index = Some(index);
2915            expected.symbols = set.symbols.to_btree_set();
2916        }
2917    }
2918}
2919
2920fn rule_first_set_inner(
2921    atn: &Atn,
2922    state_number: usize,
2923    rule_stop_state: usize,
2924    ctx: &mut FirstSetCtx<'_>,
2925    visited: &mut BTreeSet<usize>,
2926    first: &mut FirstSet,
2927) {
2928    if !visited.insert(state_number) {
2929        return;
2930    }
2931    if state_number == rule_stop_state {
2932        first.nullable = true;
2933        return;
2934    }
2935    let Some(state) = atn.state(state_number) else {
2936        return;
2937    };
2938    for transition in &state.transitions() {
2939        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2940        if !transition_symbols.is_empty() {
2941            first.symbols.extend_iter(transition_symbols);
2942            continue;
2943        }
2944        match &transition.data() {
2945            Transition::Epsilon { target }
2946            | Transition::Action { target, .. }
2947            | Transition::Predicate { target, .. }
2948            | Transition::Precedence { target, .. } => {
2949                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
2950            }
2951            Transition::Rule {
2952                target,
2953                rule_index,
2954                follow_state,
2955                ..
2956            } => {
2957                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2958                    continue;
2959                };
2960                let child_key = (*target, child_stop);
2961                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
2962                    ctx.hit_cycle = true;
2963                }
2964                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
2965                first.symbols.extend_from(&child.symbols);
2966                if child.nullable {
2967                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
2968                }
2969            }
2970            Transition::Atom { .. }
2971            | Transition::Range { .. }
2972            | Transition::Set { .. }
2973            | Transition::NotSet { .. }
2974            | Transition::Wildcard { .. } => {}
2975        }
2976    }
2977}
2978
2979/// Returns token types that can resume parsing from `state_number` after a
2980/// failed child rule, following rule calls as well as epsilon transitions.
2981fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
2982    let mut symbols = BTreeSet::new();
2983    state_sync_symbols_inner(
2984        atn,
2985        state_number,
2986        stop_state,
2987        &mut BTreeSet::new(),
2988        &mut symbols,
2989    );
2990    symbols
2991}
2992
2993/// Walks epsilon-like continuations from a parent follow state until it finds
2994/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
2995fn state_sync_symbols_inner(
2996    atn: &Atn,
2997    state_number: usize,
2998    stop_state: usize,
2999    visited: &mut BTreeSet<usize>,
3000    symbols: &mut BTreeSet<i32>,
3001) {
3002    if !visited.insert(state_number) {
3003        return;
3004    }
3005    if state_number == stop_state {
3006        symbols.insert(TOKEN_EOF);
3007        return;
3008    }
3009    let Some(state) = atn.state(state_number) else {
3010        return;
3011    };
3012    for transition in &state.transitions() {
3013        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3014        if transition_symbols.is_empty() {
3015            match &transition.data() {
3016                Transition::Rule { target, .. }
3017                | Transition::Epsilon { target }
3018                | Transition::Action { target, .. }
3019                | Transition::Predicate { target, .. }
3020                | Transition::Precedence { target, .. } => {
3021                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
3022                }
3023                Transition::Atom { .. }
3024                | Transition::Range { .. }
3025                | Transition::Set { .. }
3026                | Transition::NotSet { .. }
3027                | Transition::Wildcard { .. } => {}
3028            }
3029        } else {
3030            symbols.extend(transition_symbols);
3031        }
3032    }
3033}
3034
3035#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3036struct OperatorSymbolReachability {
3037    /// One token completes an unconditional operator token-prefix.
3038    single_token: bool,
3039    /// An unconditional operator path requires more tokens before its operand.
3040    multi_token: bool,
3041    /// At least one matching operator path depends on a semantic predicate.
3042    predicate_dependent: bool,
3043}
3044
3045impl OperatorSymbolReachability {
3046    const ADAPTIVE_FALLBACK: Self = Self {
3047        single_token: false,
3048        multi_token: false,
3049        predicate_dependent: true,
3050    };
3051
3052    const fn single_token(predicate_dependent: bool) -> Self {
3053        if predicate_dependent {
3054            Self {
3055                single_token: false,
3056                multi_token: false,
3057                predicate_dependent: true,
3058            }
3059        } else {
3060            Self {
3061                single_token: true,
3062                multi_token: false,
3063                predicate_dependent: false,
3064            }
3065        }
3066    }
3067
3068    const fn multi_token(predicate_dependent: bool) -> Self {
3069        if predicate_dependent {
3070            Self {
3071                single_token: false,
3072                multi_token: false,
3073                predicate_dependent: true,
3074            }
3075        } else {
3076            Self {
3077                single_token: false,
3078                multi_token: true,
3079                predicate_dependent: false,
3080            }
3081        }
3082    }
3083
3084    const fn union(self, other: Self) -> Self {
3085        Self {
3086            single_token: self.single_token || other.single_token,
3087            multi_token: self.multi_token || other.multi_token,
3088            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3089        }
3090    }
3091}
3092
3093#[derive(Clone, Copy)]
3094struct OperatorReachabilityRequest {
3095    symbol: i32,
3096    precedence: i32,
3097    predicate_dependent: bool,
3098    operator_rule_index: usize,
3099}
3100
3101#[derive(Clone, Copy, Debug)]
3102struct OperatorRuleContinuation {
3103    stop_state: usize,
3104    follow_state: usize,
3105    return_precedence: i32,
3106}
3107
3108struct NullablePrecedenceCtx {
3109    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3110    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3111    hit_cycle: bool,
3112}
3113
3114fn state_is_nullable_with_precedence(
3115    atn: &Atn,
3116    state_number: usize,
3117    stop_state_number: usize,
3118    precedence: i32,
3119    allow_predicates: bool,
3120    ctx: &mut NullablePrecedenceCtx,
3121) -> bool {
3122    let saved_hit_cycle = ctx.hit_cycle;
3123    ctx.hit_cycle = false;
3124    let nullable = state_is_nullable_with_precedence_cached(
3125        atn,
3126        state_number,
3127        stop_state_number,
3128        precedence,
3129        allow_predicates,
3130        ctx,
3131    );
3132    ctx.hit_cycle = saved_hit_cycle;
3133    nullable
3134}
3135
3136fn state_is_nullable_with_precedence_cached(
3137    atn: &Atn,
3138    state_number: usize,
3139    stop_state_number: usize,
3140    precedence: i32,
3141    allow_predicates: bool,
3142    ctx: &mut NullablePrecedenceCtx,
3143) -> bool {
3144    if state_number == stop_state_number {
3145        return true;
3146    }
3147    let key = (
3148        state_number,
3149        stop_state_number,
3150        precedence,
3151        allow_predicates,
3152    );
3153    if let Some(cached) = ctx.cache.get(&key) {
3154        return *cached;
3155    }
3156    if !ctx.in_progress.insert(key) {
3157        ctx.hit_cycle = true;
3158        return false;
3159    }
3160    let saved_hit_cycle = ctx.hit_cycle;
3161    ctx.hit_cycle = false;
3162    let nullable = atn.state(state_number).is_some_and(|state| {
3163        state
3164            .transitions()
3165            .iter()
3166            .any(|transition| match &transition.data() {
3167                Transition::Rule {
3168                    target,
3169                    rule_index,
3170                    follow_state,
3171                    precedence: rule_precedence,
3172                } => {
3173                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3174                        return false;
3175                    };
3176                    state_is_nullable_with_precedence_cached(
3177                        atn,
3178                        *target,
3179                        child_stop,
3180                        *rule_precedence,
3181                        allow_predicates,
3182                        ctx,
3183                    ) && state_is_nullable_with_precedence_cached(
3184                        atn,
3185                        *follow_state,
3186                        stop_state_number,
3187                        precedence,
3188                        allow_predicates,
3189                        ctx,
3190                    )
3191                }
3192                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3193                    state_is_nullable_with_precedence_cached(
3194                        atn,
3195                        *target,
3196                        stop_state_number,
3197                        precedence,
3198                        allow_predicates,
3199                        ctx,
3200                    )
3201                }
3202                Transition::Predicate { target, .. } if allow_predicates => {
3203                    state_is_nullable_with_precedence_cached(
3204                        atn,
3205                        *target,
3206                        stop_state_number,
3207                        precedence,
3208                        allow_predicates,
3209                        ctx,
3210                    )
3211                }
3212                Transition::Precedence {
3213                    target,
3214                    precedence: transition_precedence,
3215                } if *transition_precedence >= precedence => {
3216                    state_is_nullable_with_precedence_cached(
3217                        atn,
3218                        *target,
3219                        stop_state_number,
3220                        precedence,
3221                        allow_predicates,
3222                        ctx,
3223                    )
3224                }
3225                Transition::Atom { .. }
3226                | Transition::Range { .. }
3227                | Transition::Set { .. }
3228                | Transition::NotSet { .. }
3229                | Transition::Wildcard { .. }
3230                | Transition::Predicate { .. }
3231                | Transition::Precedence { .. } => false,
3232            })
3233    });
3234    ctx.in_progress.remove(&key);
3235    if !ctx.hit_cycle {
3236        ctx.cache.insert(key, nullable);
3237    }
3238    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3239    nullable
3240}
3241
3242/// Classifies what remains after the operator's first token is matched.
3243fn state_operator_token_prefix_reachability(
3244    atn: &Atn,
3245    state_number: usize,
3246    request: OperatorReachabilityRequest,
3247    continuations: &[OperatorRuleContinuation],
3248    visited: &mut BTreeSet<(usize, i32, bool)>,
3249) -> OperatorSymbolReachability {
3250    let key = (
3251        state_number,
3252        request.precedence,
3253        request.predicate_dependent,
3254    );
3255    if !visited.insert(key) {
3256        // Recursive helper rules can grow the return stack without consuming
3257        // input. Delegate cycles to adaptive prediction instead of forcing a
3258        // potentially incomplete one-token answer.
3259        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3260    }
3261    if let Some((continuation, remaining)) = continuations.split_last()
3262        && state_number == continuation.stop_state
3263    {
3264        let result = state_operator_token_prefix_reachability(
3265            atn,
3266            continuation.follow_state,
3267            OperatorReachabilityRequest {
3268                precedence: continuation.return_precedence,
3269                ..request
3270            },
3271            remaining,
3272            visited,
3273        );
3274        visited.remove(&key);
3275        return result;
3276    }
3277    let Some(state) = atn.state(state_number) else {
3278        visited.remove(&key);
3279        return OperatorSymbolReachability::default();
3280    };
3281    let completes_operator = match state.kind() {
3282        AtnStateKind::RuleStop => continuations.is_empty(),
3283        AtnStateKind::StarLoopBack
3284        | AtnStateKind::StarLoopEntry
3285        | AtnStateKind::PlusLoopBack
3286        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3287        _ => false,
3288    };
3289    if completes_operator {
3290        visited.remove(&key);
3291        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3292    }
3293    let mut reachability = OperatorSymbolReachability::default();
3294    for transition in &state.transitions() {
3295        let transition_reachability = match &transition.data() {
3296            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3297                OperatorSymbolReachability::single_token(request.predicate_dependent)
3298            }
3299            Transition::Rule {
3300                target,
3301                rule_index,
3302                follow_state,
3303                precedence: rule_precedence,
3304            } => {
3305                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3306                    continue;
3307                };
3308                let mut nested = continuations.to_vec();
3309                nested.push(OperatorRuleContinuation {
3310                    stop_state: child_stop,
3311                    follow_state: *follow_state,
3312                    return_precedence: request.precedence,
3313                });
3314                state_operator_token_prefix_reachability(
3315                    atn,
3316                    *target,
3317                    OperatorReachabilityRequest {
3318                        precedence: *rule_precedence,
3319                        ..request
3320                    },
3321                    &nested,
3322                    visited,
3323                )
3324            }
3325            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3326                state_operator_token_prefix_reachability(
3327                    atn,
3328                    *target,
3329                    request,
3330                    continuations,
3331                    visited,
3332                )
3333            }
3334            Transition::Precedence {
3335                target,
3336                precedence: transition_precedence,
3337            } => {
3338                if *transition_precedence < request.precedence {
3339                    OperatorSymbolReachability::default()
3340                } else {
3341                    state_operator_token_prefix_reachability(
3342                        atn,
3343                        *target,
3344                        request,
3345                        continuations,
3346                        visited,
3347                    )
3348                }
3349            }
3350            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3351                atn,
3352                *target,
3353                OperatorReachabilityRequest {
3354                    predicate_dependent: true,
3355                    ..request
3356                },
3357                continuations,
3358                visited,
3359            ),
3360            Transition::Atom { .. }
3361            | Transition::Range { .. }
3362            | Transition::Set { .. }
3363            | Transition::NotSet { .. }
3364            | Transition::Wildcard { .. } => {
3365                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3366            }
3367        };
3368        reachability = reachability.union(transition_reachability);
3369    }
3370    visited.remove(&key);
3371    reachability
3372}
3373
3374fn state_can_reach_symbol_with_precedence(
3375    atn: &Atn,
3376    state_number: usize,
3377    request: OperatorReachabilityRequest,
3378    nullable_ctx: &mut NullablePrecedenceCtx,
3379    continuations: &mut Vec<OperatorRuleContinuation>,
3380    visited: &mut BTreeSet<(usize, i32, bool)>,
3381) -> OperatorSymbolReachability {
3382    let key = (
3383        state_number,
3384        request.precedence,
3385        request.predicate_dependent,
3386    );
3387    if !visited.insert(key) {
3388        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3389    }
3390    let Some(state) = atn.state(state_number) else {
3391        visited.remove(&key);
3392        return OperatorSymbolReachability::default();
3393    };
3394    let mut reachability = OperatorSymbolReachability::default();
3395    for transition in &state.transitions() {
3396        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3397            reachability = reachability.union(state_operator_token_prefix_reachability(
3398                atn,
3399                transition.target(),
3400                request,
3401                continuations,
3402                &mut BTreeSet::new(),
3403            ));
3404            continue;
3405        }
3406        let transition_reachability = match &transition.data() {
3407            Transition::Rule {
3408                target,
3409                rule_index,
3410                follow_state,
3411                precedence: rule_precedence,
3412            } => {
3413                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3414                    continue;
3415                };
3416                continuations.push(OperatorRuleContinuation {
3417                    stop_state: child_stop,
3418                    follow_state: *follow_state,
3419                    return_precedence: request.precedence,
3420                });
3421                let mut result = state_can_reach_symbol_with_precedence(
3422                    atn,
3423                    *target,
3424                    OperatorReachabilityRequest {
3425                        precedence: *rule_precedence,
3426                        ..request
3427                    },
3428                    nullable_ctx,
3429                    continuations,
3430                    visited,
3431                );
3432                continuations.pop();
3433                if state_is_nullable_with_precedence(
3434                    atn,
3435                    *target,
3436                    child_stop,
3437                    *rule_precedence,
3438                    true,
3439                    nullable_ctx,
3440                ) {
3441                    let child_predicate_dependent = request.predicate_dependent
3442                        || !state_is_nullable_with_precedence(
3443                            atn,
3444                            *target,
3445                            child_stop,
3446                            *rule_precedence,
3447                            false,
3448                            nullable_ctx,
3449                        );
3450                    result = result.union(state_can_reach_symbol_with_precedence(
3451                        atn,
3452                        *follow_state,
3453                        OperatorReachabilityRequest {
3454                            predicate_dependent: child_predicate_dependent,
3455                            ..request
3456                        },
3457                        nullable_ctx,
3458                        continuations,
3459                        visited,
3460                    ));
3461                }
3462                result
3463            }
3464            Transition::Epsilon { target }
3465            | Transition::Action { target, .. }
3466            | Transition::Precedence { target, .. } => {
3467                if matches!(
3468                    &transition.data(),
3469                    Transition::Precedence {
3470                        precedence: transition_precedence,
3471                        ..
3472                    } if *transition_precedence < request.precedence
3473                ) {
3474                    continue;
3475                }
3476                state_can_reach_symbol_with_precedence(
3477                    atn,
3478                    *target,
3479                    request,
3480                    nullable_ctx,
3481                    continuations,
3482                    visited,
3483                )
3484            }
3485            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3486                atn,
3487                *target,
3488                OperatorReachabilityRequest {
3489                    predicate_dependent: true,
3490                    ..request
3491                },
3492                nullable_ctx,
3493                continuations,
3494                visited,
3495            ),
3496            Transition::Atom { .. }
3497            | Transition::Range { .. }
3498            | Transition::Set { .. }
3499            | Transition::NotSet { .. }
3500            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3501        };
3502        reachability = reachability.union(transition_reachability);
3503    }
3504    visited.remove(&key);
3505    reachability
3506}
3507
3508fn left_recursive_operator_lookahead(
3509    atn: &Atn,
3510    state_number: usize,
3511    precedence: i32,
3512) -> LeftRecursiveOperatorLookahead {
3513    let Some(state) = atn.state(state_number) else {
3514        return LeftRecursiveOperatorLookahead::default();
3515    };
3516    let Some(operator_rule_index) = state.rule_index() else {
3517        return LeftRecursiveOperatorLookahead::default();
3518    };
3519    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3520    let mut nullable_ctx = NullablePrecedenceCtx {
3521        cache: FxHashMap::default(),
3522        in_progress: BTreeSet::new(),
3523        hit_cycle: false,
3524    };
3525    for transition in &state.transitions() {
3526        let target = transition.target();
3527        if atn
3528            .state(target)
3529            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3530        {
3531            continue;
3532        }
3533        for symbol in 1..=atn.max_token_type() {
3534            let reachability = state_can_reach_symbol_with_precedence(
3535                atn,
3536                target,
3537                OperatorReachabilityRequest {
3538                    symbol,
3539                    precedence,
3540                    predicate_dependent: false,
3541                    operator_rule_index,
3542                },
3543                &mut nullable_ctx,
3544                &mut Vec::new(),
3545                &mut BTreeSet::new(),
3546            );
3547            if reachability.single_token {
3548                lookahead.single_token.insert(symbol);
3549            }
3550            if reachability.multi_token {
3551                lookahead.multi_token_prefix.insert(symbol);
3552            }
3553            if reachability.predicate_dependent {
3554                lookahead.predicate_dependent.insert(symbol);
3555            }
3556        }
3557    }
3558    lookahead
3559}
3560
3561#[derive(Debug, Default)]
3562struct StateBeforeStopLookahead {
3563    symbols: TokenBitSet,
3564    reaches_context_boundary: bool,
3565}
3566
3567fn state_before_stop_lookahead(
3568    atn: &Atn,
3569    state_number: usize,
3570    stop_state_number: usize,
3571) -> Rc<StateBeforeStopLookahead> {
3572    with_shared_atn_caches(atn, |cache| {
3573        let key = (state_number, stop_state_number);
3574        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3575            return Rc::clone(cached);
3576        }
3577        let mut lookahead = StateBeforeStopLookahead::default();
3578        state_before_stop_lookahead_inner(
3579            atn,
3580            state_number,
3581            stop_state_number,
3582            &mut BTreeSet::new(),
3583            &mut cache.first_set,
3584            &mut lookahead,
3585        );
3586        let lookahead = Rc::new(lookahead);
3587        cache
3588            .state_before_stop_lookahead
3589            .insert(key, Rc::clone(&lookahead));
3590        lookahead
3591    })
3592}
3593
3594fn state_before_stop_lookahead_inner(
3595    atn: &Atn,
3596    state_number: usize,
3597    stop_state_number: usize,
3598    visited: &mut BTreeSet<usize>,
3599    first_set_cache: &mut FirstSetCache,
3600    lookahead: &mut StateBeforeStopLookahead,
3601) {
3602    if state_number == stop_state_number {
3603        lookahead.reaches_context_boundary = true;
3604        return;
3605    }
3606    if !visited.insert(state_number) {
3607        return;
3608    }
3609    let Some(state) = atn.state(state_number) else {
3610        return;
3611    };
3612    if state.kind() == AtnStateKind::RuleStop {
3613        lookahead.reaches_context_boundary = true;
3614        return;
3615    }
3616    for transition in &state.transitions() {
3617        match &transition.data() {
3618            Transition::Epsilon { target }
3619            | Transition::Action { target, .. }
3620            | Transition::Predicate { target, .. }
3621            | Transition::Precedence { target, .. } => {
3622                state_before_stop_lookahead_inner(
3623                    atn,
3624                    *target,
3625                    stop_state_number,
3626                    visited,
3627                    first_set_cache,
3628                    lookahead,
3629                );
3630            }
3631            Transition::Rule {
3632                target,
3633                rule_index,
3634                follow_state,
3635                ..
3636            } => {
3637                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3638                    continue;
3639                };
3640                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3641                lookahead.symbols.extend_from(&child.symbols);
3642                if child.nullable {
3643                    state_before_stop_lookahead_inner(
3644                        atn,
3645                        *follow_state,
3646                        stop_state_number,
3647                        visited,
3648                        first_set_cache,
3649                        lookahead,
3650                    );
3651                }
3652            }
3653            Transition::Atom { .. }
3654            | Transition::Range { .. }
3655            | Transition::Set { .. }
3656            | Transition::NotSet { .. }
3657            | Transition::Wildcard { .. } => {
3658                lookahead.symbols.extend_iter(transition_expected_symbols(
3659                    transition,
3660                    atn.max_token_type(),
3661                ));
3662            }
3663        }
3664    }
3665}
3666
3667fn caller_context_can_match_symbol_before_state(
3668    atn: &Atn,
3669    return_states: impl DoubleEndedIterator<Item = usize>,
3670    stop_state_number: usize,
3671    symbol: i32,
3672) -> bool {
3673    for return_state in return_states.rev() {
3674        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3675        if lookahead.symbols.contains(symbol) {
3676            return true;
3677        }
3678        if !lookahead.reaches_context_boundary {
3679            return false;
3680        }
3681    }
3682    false
3683}
3684
3685/// Carries recovery expectations and their restart state through epsilon-only
3686/// paths. ANTLR can report and repair at the decision state even when the
3687/// failed consuming transition is nested under block or loop epsilon edges.
3688fn next_recovery_context(
3689    atn: &Atn,
3690    state: AtnState<'_>,
3691    inherited: &BTreeSet<i32>,
3692    inherited_state: Option<usize>,
3693) -> (BTreeSet<i32>, Option<usize>) {
3694    let state_symbols = state_expected_symbols(atn, state.state_number());
3695    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3696        let mut symbols = state_symbols;
3697        symbols.extend(inherited.iter().copied());
3698        return (symbols, Some(state.state_number()));
3699    }
3700    (inherited.clone(), inherited_state)
3701}
3702
3703fn recovery_expected_symbols(
3704    atn: &Atn,
3705    state_number: usize,
3706    inherited: &BTreeSet<i32>,
3707) -> BTreeSet<i32> {
3708    let mut symbols = state_expected_symbols(atn, state_number);
3709    symbols.extend(inherited.iter().copied());
3710    symbols
3711}
3712
3713/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3714/// parser's cached state-expected-symbols sets and the inherited `Rc`
3715/// without copying when the state cannot widen recovery.
3716fn fast_next_recovery_context<S, H>(
3717    parser: &mut BaseParser<S, H>,
3718    atn: &Atn,
3719    state: AtnState<'_>,
3720    inherited: &Rc<BTreeSet<i32>>,
3721    inherited_state: Option<usize>,
3722) -> (Rc<BTreeSet<i32>>, Option<usize>)
3723where
3724    S: TokenSource,
3725    H: SemanticHooks,
3726{
3727    if state.transitions().len() <= 1 {
3728        return (Rc::clone(inherited), inherited_state);
3729    }
3730    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3731    if state_symbols.is_empty() {
3732        return (Rc::clone(inherited), inherited_state);
3733    }
3734    if inherited.is_empty() {
3735        return (state_symbols, Some(state.state_number()));
3736    }
3737    if Rc::ptr_eq(&state_symbols, inherited) {
3738        return (state_symbols, Some(state.state_number()));
3739    }
3740    let mut combined = (*state_symbols).clone();
3741    combined.extend(inherited.iter().copied());
3742    (
3743        parser.intern_recovery_symbols(combined),
3744        Some(state.state_number()),
3745    )
3746}
3747
3748/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3749/// cached state-expected-symbols and avoids cloning when no widening is
3750/// needed.
3751fn fast_recovery_expected_symbols<S, H>(
3752    parser: &mut BaseParser<S, H>,
3753    atn: &Atn,
3754    state_number: usize,
3755    inherited: &Rc<BTreeSet<i32>>,
3756) -> Rc<BTreeSet<i32>>
3757where
3758    S: TokenSource,
3759    H: SemanticHooks,
3760{
3761    let cached = parser.cached_state_expected_symbols(atn, state_number);
3762    if inherited.is_empty() {
3763        return cached;
3764    }
3765    if cached.is_empty() {
3766        return Rc::clone(inherited);
3767    }
3768    if Rc::ptr_eq(&cached, inherited) {
3769        return cached;
3770    }
3771    let mut combined = (*cached).clone();
3772    combined.extend(inherited.iter().copied());
3773    parser.intern_recovery_symbols(combined)
3774}
3775
3776struct ParserTableSemCtx<'a> {
3777    member_values: &'a mut BTreeMap<usize, i64>,
3778    return_values: &'a mut BTreeMap<String, i64>,
3779}
3780
3781impl semir::PredContext for ParserTableSemCtx<'_> {
3782    type TokenText<'a>
3783        = &'a str
3784    where
3785        Self: 'a;
3786
3787    fn la(&mut self, _offset: isize) -> i64 {
3788        i64::from(TOKEN_EOF)
3789    }
3790
3791    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3792        None
3793    }
3794
3795    fn token_index_adjacent(&mut self) -> bool {
3796        false
3797    }
3798
3799    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3800        None
3801    }
3802
3803    fn member(&self, member: usize) -> Option<i64> {
3804        Some(self.member_values.get(&member).copied().unwrap_or_default())
3805    }
3806
3807    fn local_arg(&self) -> Option<i64> {
3808        None
3809    }
3810
3811    fn column(&self) -> Option<i64> {
3812        None
3813    }
3814
3815    fn token_start_column(&self) -> Option<i64> {
3816        None
3817    }
3818
3819    fn token_text_so_far(&self) -> Option<String> {
3820        None
3821    }
3822
3823    fn hook(&mut self, _hook: HookId) -> bool {
3824        false
3825    }
3826}
3827
3828impl semir::ActContext for ParserTableSemCtx<'_> {
3829    fn set_member(&mut self, member: usize, value: i64) {
3830        self.member_values.insert(member, value);
3831    }
3832
3833    fn set_return(&mut self, name: &str, value: i64) {
3834        self.return_values.insert(name.to_owned(), value);
3835    }
3836
3837    fn action_hook(&mut self, _hook: HookId) {}
3838}
3839
3840/// Applies generated integer-member side effects to one speculative path.
3841fn apply_member_actions(
3842    source_state: usize,
3843    actions: &[ParserMemberAction],
3844    semantics: Option<&ParserSemantics>,
3845    values: &mut BTreeMap<usize, i64>,
3846) {
3847    for action in actions
3848        .iter()
3849        .filter(|action| action.source_state == source_state)
3850    {
3851        *values.entry(action.member).or_default() += action.delta;
3852    }
3853    let Some(semantics) = semantics else {
3854        return;
3855    };
3856    let mut return_values = BTreeMap::new();
3857    let mut ctx = ParserTableSemCtx {
3858        member_values: values,
3859        return_values: &mut return_values,
3860    };
3861    for action in semantics
3862        .actions
3863        .iter()
3864        .filter(|action| action.source_state == source_state && action.speculative)
3865    {
3866        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3867    }
3868}
3869
3870/// Returns the speculative member state after replaying one ATN action state.
3871fn member_values_after_action(
3872    source_state: usize,
3873    actions: &[ParserMemberAction],
3874    semantics: Option<&ParserSemantics>,
3875    values: &BTreeMap<usize, i64>,
3876) -> BTreeMap<usize, i64> {
3877    let mut values = values.clone();
3878    apply_member_actions(source_state, actions, semantics, &mut values);
3879    values
3880}
3881
3882/// Returns the speculative rule-return state after replaying one ATN action.
3883fn return_values_after_action(
3884    source_state: usize,
3885    rule_index: usize,
3886    actions: &[ParserReturnAction],
3887    semantics: Option<&ParserSemantics>,
3888    values: &BTreeMap<String, i64>,
3889) -> BTreeMap<String, i64> {
3890    let mut values = values.clone();
3891    for action in actions
3892        .iter()
3893        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
3894    {
3895        values.insert(action.name.to_owned(), action.value);
3896    }
3897    if let Some(semantics) = semantics {
3898        let mut member_values = BTreeMap::new();
3899        let mut ctx = ParserTableSemCtx {
3900            member_values: &mut member_values,
3901            return_values: &mut values,
3902        };
3903        for action in semantics.actions.iter().filter(|action| {
3904            action.source_state == source_state
3905                && action.rule_index == rule_index
3906                && !action.speculative
3907        }) {
3908            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3909        }
3910    }
3911    values
3912}
3913
3914/// Resolves the integer argument visible to a child rule invocation.
3915fn rule_local_int_arg(
3916    rule_args: &[ParserRuleArg],
3917    source_state: usize,
3918    rule_index: usize,
3919    local_int_arg: Option<(usize, i64)>,
3920) -> Option<(usize, i64)> {
3921    rule_args
3922        .iter()
3923        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
3924        .map(|arg| {
3925            let value = if arg.inherit_local {
3926                local_int_arg.map_or(arg.value, |(_, value)| value)
3927            } else {
3928                arg.value
3929            };
3930            (rule_index, value)
3931        })
3932}
3933
3934/// Builds the terminal recognition outcome for a path that reached its stop
3935/// state.
3936fn stop_outcome(
3937    index: usize,
3938    consumed_eof: bool,
3939    rule_alt_number: usize,
3940    member_values: BTreeMap<usize, i64>,
3941    return_values: BTreeMap<String, i64>,
3942) -> Vec<RecognizeOutcome> {
3943    vec![RecognizeOutcome {
3944        index,
3945        consumed_eof,
3946        alt_number: rule_alt_number,
3947        member_values,
3948        return_values,
3949        diagnostics: DiagnosticSeqId::EMPTY,
3950        decisions: Vec::new(),
3951        actions: Vec::new(),
3952        nodes: NodeSeqId::EMPTY,
3953    }]
3954}
3955
3956fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
3957    with_shared_atn_caches(atn, |cache| {
3958        *cache.observable_action_transitions.get_or_insert_with(|| {
3959            atn.states().any(|state| {
3960                state.transitions().iter().any(|transition| {
3961                    matches!(
3962                        &transition.data(),
3963                        Transition::Action {
3964                            action_index: Some(_),
3965                            ..
3966                        }
3967                    )
3968                })
3969            })
3970        })
3971    })
3972}
3973
3974fn atn_has_predicate_transitions(atn: &Atn) -> bool {
3975    with_shared_atn_caches(atn, |cache| {
3976        *cache.predicate_transitions.get_or_insert_with(|| {
3977            atn.states().any(|state| {
3978                state
3979                    .transitions()
3980                    .iter()
3981                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
3982            })
3983        })
3984    })
3985}
3986
3987/// Reports whether predicates are the only observable semantics the fast
3988/// recognizer must preserve. Without path-local actions, arguments, or return
3989/// state, repeated evaluation at one coordinate and input index receives the
3990/// same runtime context.
3991fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
3992    options.init_action_rules.is_empty()
3993        && !options.track_alt_numbers
3994        && options
3995            .predicates
3996            .iter()
3997            .all(|(_, _, predicate)| predicate.failure_message().is_none())
3998        && options.semantics.is_none_or(|semantics| {
3999            semantics.actions.is_empty()
4000                && semantics
4001                    .predicates
4002                    .iter()
4003                    .all(|predicate| predicate.failure_message.is_none())
4004        })
4005        && options.rule_args.is_empty()
4006        && options.member_actions.is_empty()
4007        && options.return_actions.is_empty()
4008        && !atn_has_observable_action_transitions(atn)
4009}
4010
4011#[derive(Clone, Debug, Eq, PartialEq)]
4012struct RecognizeRequest<'a> {
4013    state_number: usize,
4014    stop_state: usize,
4015    index: usize,
4016    rule_start_index: usize,
4017    decision_start_index: Option<usize>,
4018    init_action_rules: &'a BTreeSet<usize>,
4019    predicates: &'a [(usize, usize, ParserPredicate)],
4020    semantics: Option<&'a ParserSemantics>,
4021    rule_args: &'a [ParserRuleArg],
4022    member_actions: &'a [ParserMemberAction],
4023    return_actions: &'a [ParserReturnAction],
4024    local_int_arg: Option<(usize, i64)>,
4025    member_values: BTreeMap<usize, i64>,
4026    return_values: BTreeMap<String, i64>,
4027    rule_alt_number: usize,
4028    track_alt_numbers: bool,
4029    consumed_eof: bool,
4030    committed_decision: bool,
4031    /// Current left-recursive precedence threshold, matching ANTLR's
4032    /// `precpred(_ctx, k)` check for generated precedence rules.
4033    precedence: i32,
4034    depth: usize,
4035    recovery_symbols: BTreeSet<i32>,
4036    recovery_state: Option<usize>,
4037}
4038
4039#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
4040struct RecognizeKey {
4041    state_number: usize,
4042    stop_state: usize,
4043    index: usize,
4044    rule_start_index: usize,
4045    decision_start_index: Option<usize>,
4046    local_int_arg: Option<(usize, i64)>,
4047    member_values: BTreeMap<usize, i64>,
4048    return_values: BTreeMap<String, i64>,
4049    rule_alt_number: usize,
4050    track_alt_numbers: bool,
4051    consumed_eof: bool,
4052    committed_decision: bool,
4053    precedence: i32,
4054    recovery_symbols: BTreeSet<i32>,
4055    recovery_state: Option<usize>,
4056}
4057
4058#[derive(Clone, Debug, Eq, PartialEq)]
4059struct EpsilonActionStep {
4060    source_state: usize,
4061    target: usize,
4062    action_rule_index: Option<usize>,
4063    left_recursive_boundary: Option<usize>,
4064    decision: Option<usize>,
4065    decision_start_index: Option<usize>,
4066    alt_number: usize,
4067    recovery_symbols: BTreeSet<i32>,
4068    recovery_state: Option<usize>,
4069}
4070
4071struct RecognizeScratch<'a> {
4072    visiting: &'a mut BTreeSet<RecognizeKey>,
4073    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4074    expected: &'a mut ExpectedTokens,
4075}
4076
4077#[derive(Clone, Debug, Eq, PartialEq)]
4078struct FastRecognizeRequest {
4079    state_number: usize,
4080    stop_state: usize,
4081    index: usize,
4082    rule_start_index: usize,
4083    decision_start_index: Option<usize>,
4084    precedence: i32,
4085    depth: usize,
4086    recovery_symbols: Rc<BTreeSet<i32>>,
4087    recovery_state: Option<usize>,
4088}
4089
4090#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4091struct FastRecognizeTopRequest {
4092    start_state: usize,
4093    stop_state: usize,
4094    start_index: usize,
4095    precedence: i32,
4096    caller_follow_state: Option<usize>,
4097}
4098
4099#[derive(Clone, Copy, Debug)]
4100struct FastPredicateContext<'a> {
4101    predicates: &'a [(usize, usize, ParserPredicate)],
4102    semantics: Option<&'a ParserSemantics>,
4103    member_values: &'a BTreeMap<usize, i64>,
4104}
4105
4106#[derive(Clone, Copy, Debug, Default)]
4107struct AltNumberTracking {
4108    public: bool,
4109    context: bool,
4110}
4111
4112impl AltNumberTracking {
4113    const fn any(self) -> bool {
4114        self.public || self.context
4115    }
4116}
4117
4118struct FastRecognizeScratch<'a, 'b> {
4119    predicate_context: Option<FastPredicateContext<'a>>,
4120    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4121    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4122    expected: &'b mut ExpectedTokens,
4123    native_depth: usize,
4124}
4125
4126#[derive(Clone, Copy, Debug)]
4127struct FastRepetitionShape {
4128    enter_target: usize,
4129    exit_target: usize,
4130    body_stop_state: usize,
4131    enter_transition_index: usize,
4132    exit_transition_index: usize,
4133}
4134
4135#[derive(Clone, Copy, Debug)]
4136struct FastRepetitionPath {
4137    index: usize,
4138    deferred_nodes: FastDeferredNodeId,
4139    diagnostics: DiagnosticSeqId,
4140    consumed_eof: bool,
4141}
4142
4143enum FastRepetitionWork {
4144    Enter(FastRepetitionPath),
4145    Exit(FastRepetitionPath),
4146}
4147
4148/// Dense entered/exited coordinate sets for one repetition walk.
4149///
4150/// The start coordinate stays inline so short loops avoid a heap allocation;
4151/// later token indexes use one byte each instead of two hash-table entries.
4152struct FastRepetitionCoordinates {
4153    base_index: usize,
4154    base_state: u8,
4155    later_states: Vec<u8>,
4156}
4157
4158impl FastRepetitionCoordinates {
4159    const ENTERED: u8 = 0;
4160    const EXITED: u8 = 2;
4161
4162    const fn new(base_index: usize) -> Self {
4163        Self {
4164            base_index,
4165            base_state: 0,
4166            later_states: Vec::new(),
4167        }
4168    }
4169
4170    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4171        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4172    }
4173
4174    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4175        self.insert(path.index, path.consumed_eof, Self::EXITED)
4176    }
4177
4178    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4179        let Some(offset) = index.checked_sub(self.base_index) else {
4180            return false;
4181        };
4182        let state = if offset == 0 {
4183            &mut self.base_state
4184        } else {
4185            if self.later_states.len() < offset {
4186                self.later_states.resize(offset, 0);
4187            }
4188            &mut self.later_states[offset - 1]
4189        };
4190        let bit = 1 << (base_bit + u8::from(consumed_eof));
4191        let is_new = *state & bit == 0;
4192        *state |= bit;
4193        is_new
4194    }
4195}
4196
4197fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4198    if state.precedence_rule_decision()
4199        || !matches!(
4200            state.kind(),
4201            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4202        )
4203        || state.transitions().len() != 2
4204    {
4205        return None;
4206    }
4207    let mut enter = None;
4208    let mut exit = None;
4209    for (index, transition) in state.transitions().iter().enumerate() {
4210        if transition.kind() != ParserTransitionKind::Epsilon {
4211            return None;
4212        }
4213        let target = transition.target();
4214        if atn
4215            .state(target)
4216            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4217        {
4218            if exit.replace((index, target)).is_some() {
4219                return None;
4220            }
4221        } else if enter.replace((index, target)).is_some() {
4222            return None;
4223        }
4224    }
4225    let (enter_transition_index, enter_target) = enter?;
4226    let (exit_transition_index, exit_target) = exit?;
4227    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4228        atn.state(exit_target)?.loop_back_state()?
4229    } else {
4230        state.state_number()
4231    };
4232    Some(FastRepetitionShape {
4233        enter_target,
4234        exit_target,
4235        body_stop_state,
4236        enter_transition_index,
4237        exit_transition_index,
4238    })
4239}
4240
4241fn push_fast_repetition_work(
4242    work: &mut Vec<FastRepetitionWork>,
4243    shape: FastRepetitionShape,
4244    path: FastRepetitionPath,
4245    lookahead: Option<&DecisionLookahead>,
4246    symbol: i32,
4247) {
4248    // Match the normal recognizer's FIRST-set pruning before queueing work.
4249    // Ambiguous body paths still share the coordinate bitmap below.
4250    let transition_is_viable = |transition_index: usize| {
4251        let Some(entry) = lookahead else {
4252            return true;
4253        };
4254        let Some(transition) = entry.transitions.get(transition_index) else {
4255            return true;
4256        };
4257        transition.nullable || transition.symbols.contains(symbol)
4258    };
4259    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4260    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4261    if shape.enter_transition_index < shape.exit_transition_index {
4262        if exit_is_viable {
4263            work.push(FastRepetitionWork::Exit(path));
4264        }
4265        if enter_is_viable {
4266            work.push(FastRepetitionWork::Enter(path));
4267        }
4268    } else {
4269        if enter_is_viable {
4270            work.push(FastRepetitionWork::Enter(path));
4271        }
4272        if exit_is_viable {
4273            work.push(FastRepetitionWork::Exit(path));
4274        }
4275    }
4276}
4277
4278/// Memo key for the fast recognizer. `recovery_symbols` must come from
4279/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4280/// key, so equal sets share one allocation and the key can store that
4281/// allocation's address instead of cloning an `Rc` and walking the full
4282/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4283/// into distinct cache coordinates.
4284#[derive(Clone, Debug)]
4285struct FastRecognizeKey {
4286    state_number: usize,
4287    stop_state: usize,
4288    index: usize,
4289    rule_start_index: usize,
4290    decision_start_index: Option<usize>,
4291    precedence: i32,
4292    recovery_symbols_id: usize,
4293    recovery_state: Option<usize>,
4294}
4295
4296impl PartialEq for FastRecognizeKey {
4297    fn eq(&self, other: &Self) -> bool {
4298        if self.state_number != other.state_number
4299            || self.stop_state != other.stop_state
4300            || self.index != other.index
4301            || self.rule_start_index != other.rule_start_index
4302            || self.decision_start_index != other.decision_start_index
4303            || self.precedence != other.precedence
4304            || self.recovery_state != other.recovery_state
4305            || self.recovery_symbols_id != other.recovery_symbols_id
4306        {
4307            return false;
4308        }
4309        true
4310    }
4311}
4312
4313impl Eq for FastRecognizeKey {}
4314
4315impl Hash for FastRecognizeKey {
4316    fn hash<H: Hasher>(&self, hasher: &mut H) {
4317        self.state_number.hash(hasher);
4318        self.stop_state.hash(hasher);
4319        self.index.hash(hasher);
4320        self.rule_start_index.hash(hasher);
4321        self.decision_start_index.hash(hasher);
4322        self.precedence.hash(hasher);
4323        self.recovery_state.hash(hasher);
4324        self.recovery_symbols_id.hash(hasher);
4325    }
4326}
4327
4328struct FastRecoveryRequest<'a, 'b> {
4329    atn: &'a Atn,
4330    transition: ParserTransition<'a>,
4331    expected_symbols: Rc<BTreeSet<i32>>,
4332    target: usize,
4333    request: FastRecognizeRequest,
4334    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4335    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4336    expected: &'b mut ExpectedTokens,
4337}
4338
4339struct FastCurrentTokenDeletionRequest<'a, 'b> {
4340    atn: &'a Atn,
4341    expected_symbols: Rc<BTreeSet<i32>>,
4342    request: FastRecognizeRequest,
4343    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4344    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4345    expected: &'b mut ExpectedTokens,
4346}
4347
4348#[derive(Clone, Copy)]
4349struct FastChildRuleFailureRecoveryRequest<'a> {
4350    atn: &'a Atn,
4351    rule_index: usize,
4352    start_index: usize,
4353    follow_state: usize,
4354    stop_state: usize,
4355    expected: &'a ExpectedTokens,
4356}
4357
4358struct RecoveryRequest<'a, 'b> {
4359    atn: &'a Atn,
4360    transition: ParserTransition<'a>,
4361    expected_symbols: BTreeSet<i32>,
4362    target: usize,
4363    request: RecognizeRequest<'a>,
4364    visiting: &'b mut BTreeSet<RecognizeKey>,
4365    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4366    expected: &'b mut ExpectedTokens,
4367}
4368
4369struct CurrentTokenDeletionRequest<'a, 'b> {
4370    atn: &'a Atn,
4371    expected_symbols: BTreeSet<i32>,
4372    request: RecognizeRequest<'a>,
4373    visiting: &'b mut BTreeSet<RecognizeKey>,
4374    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4375    expected: &'b mut ExpectedTokens,
4376}
4377
4378/// Carries the state needed after the normal token-recovery strategies fail
4379/// for a consuming transition.
4380struct ConsumingFailureFallback<'a> {
4381    atn: &'a Atn,
4382    target: usize,
4383    request: RecognizeRequest<'a>,
4384    symbol: i32,
4385    expected_symbols: BTreeSet<i32>,
4386    decision_start_index: Option<usize>,
4387    decision: Option<usize>,
4388}
4389
4390/// Captures the parent-rule context needed when a called rule fails before it
4391/// can produce a normal outcome.
4392struct ChildRuleFailureRecovery<'a> {
4393    atn: &'a Atn,
4394    rule_index: usize,
4395    start_index: usize,
4396    follow_state: usize,
4397    stop_state: usize,
4398    member_values: BTreeMap<usize, i64>,
4399    expected: &'a ExpectedTokens,
4400}
4401
4402/// Bundles the context needed to evaluate one semantic predicate transition.
4403#[derive(Clone, Copy, Debug)]
4404struct PredicateEval<'a> {
4405    index: usize,
4406    rule_index: usize,
4407    pred_index: usize,
4408    predicates: &'a [(usize, usize, ParserPredicate)],
4409    semantics: Option<&'a ParserSemantics>,
4410    context: Option<&'a ParserRuleContext>,
4411    local_int_arg: Option<(usize, i64)>,
4412    member_values: &'a BTreeMap<usize, i64>,
4413}
4414
4415#[derive(Clone, Copy, Debug)]
4416struct ParserSemanticHookRequest<'a> {
4417    index: usize,
4418    rule_index: usize,
4419    pred_index: usize,
4420    context: Option<&'a ParserRuleContext>,
4421    local_int_arg: Option<(usize, i64)>,
4422    member_values: &'a BTreeMap<usize, i64>,
4423}
4424
4425/// Predicate-evaluation context over the recognizer's speculative state.
4426///
4427/// This sits in the prediction hot loop, so everything is borrowed: member
4428/// state read-only from the current speculative path and the rule name
4429/// straight from recognizer metadata. Predicates are pure by construction
4430/// ([`semir::PExpr`] has no mutating node); statement execution uses
4431/// [`ParserTableSemCtx`] (speculative member/return replay) and
4432/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4433struct ParserSemIrCtx<'a, S, H>
4434where
4435    S: TokenSource,
4436    H: SemanticHooks,
4437{
4438    input: &'a mut CommonTokenStream<S>,
4439    tree_storage: &'a ParseTreeStorage,
4440    semantic_hooks: &'a mut H,
4441    rule_index: usize,
4442    coordinate_index: usize,
4443    rule_name: Option<&'a str>,
4444    context: Option<&'a ParserRuleContext>,
4445    local_int_arg: Option<(usize, i64)>,
4446    member_values: &'a BTreeMap<usize, i64>,
4447    invoked_predicates: &'a mut Vec<(usize, usize)>,
4448    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4449    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4450    /// table path instead of coercing the miss to `false`.
4451    unknown_predicate_policy: UnknownSemanticPolicy,
4452    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4453}
4454
4455impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4456where
4457    S: TokenSource,
4458    H: SemanticHooks,
4459{
4460    type TokenText<'a>
4461        = TokenView<'a>
4462    where
4463        Self: 'a;
4464
4465    fn la(&mut self, offset: isize) -> i64 {
4466        i64::from(self.input.la(offset))
4467    }
4468
4469    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4470        self.input.lt(offset)
4471    }
4472
4473    fn token_index_adjacent(&mut self) -> bool {
4474        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4475            return false;
4476        };
4477        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4478            return false;
4479        };
4480        first + 1 == second
4481    }
4482
4483    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4484        self.context.and_then(|context| {
4485            context
4486                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4487                .next()
4488                .map(crate::tree::RuleNodeView::text)
4489        })
4490    }
4491
4492    fn member(&self, member: usize) -> Option<i64> {
4493        Some(self.member_values.get(&member).copied().unwrap_or_default())
4494    }
4495
4496    fn local_arg(&self) -> Option<i64> {
4497        self.local_int_arg.map(|(_, value)| value)
4498    }
4499
4500    fn column(&self) -> Option<i64> {
4501        None
4502    }
4503
4504    fn token_start_column(&self) -> Option<i64> {
4505        None
4506    }
4507
4508    fn token_text_so_far(&self) -> Option<String> {
4509        None
4510    }
4511
4512    fn hook(&mut self, _hook: HookId) -> bool {
4513        let mut ctx = ParserSemCtx {
4514            input: &mut *self.input,
4515            tree_storage: self.tree_storage,
4516            rule_index: self.rule_index,
4517            coordinate_index: self.coordinate_index,
4518            rule_name: self.rule_name.map(str::to_owned),
4519            context: self.context,
4520            tree: None,
4521            local_int_arg: self.local_int_arg,
4522            member_values: self.member_values,
4523            action: None,
4524        };
4525        match self
4526            .semantic_hooks
4527            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4528        {
4529            Some(result) => result,
4530            // No hook answered this coordinate: fall through to the configured
4531            // policy instead of silently rejecting the alternative, matching the
4532            // legacy table path's dispatch chain (hook → policy).
4533            None => apply_unknown_predicate_policy(
4534                self.unknown_predicate_policy,
4535                self.rule_index,
4536                self.coordinate_index,
4537                self.unknown_predicate_hits,
4538            ),
4539        }
4540    }
4541
4542    fn trace_bool(&mut self, value: bool) -> bool {
4543        let key = (self.rule_index, self.coordinate_index);
4544        if !self.invoked_predicates.contains(&key) {
4545            self.invoked_predicates.push(key);
4546            use std::io::Write as _;
4547            let mut stdout = std::io::stdout().lock();
4548            let _ = writeln!(stdout, "eval={value}");
4549        }
4550        value
4551    }
4552}
4553
4554/// Captures predicate-failure recovery metadata for fail-option predicates.
4555struct PredicateFailureRecovery<'a> {
4556    rule_index: usize,
4557    index: usize,
4558    message: &'a str,
4559    member_values: BTreeMap<usize, i64>,
4560    return_values: BTreeMap<String, i64>,
4561    rule_alt_number: usize,
4562}
4563
4564#[derive(Debug)]
4565enum DirectAdaptiveParseControl {
4566    Fallback(DirectAdaptiveFallback),
4567}
4568
4569#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4570enum DirectAdaptiveFallback {
4571    Action,
4572    InvalidAlt,
4573    LeftRecursiveBoundary,
4574    MissingAtn,
4575    NoTransition,
4576    Predicate,
4577    Prediction,
4578    Precedence,
4579    RuleStop,
4580    SemanticContext,
4581    StepLimit,
4582    TokenMismatch,
4583    UnknownDecision,
4584}
4585
4586type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4587
4588struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4589where
4590    S: TokenSource,
4591    H: SemanticHooks,
4592{
4593    parser: &'sim mut BaseParser<S, H>,
4594    atn: &'atn Atn,
4595    simulator: &'sim mut ParserAtnSimulator<'atn>,
4596    decision_by_state: Vec<Option<usize>>,
4597    steps: usize,
4598}
4599
4600/// Outcome of a generated token / set / not-set match that may recover.
4601///
4602/// Generated parsers append `children` to the current rule context. `consumed_eof`
4603/// reports whether the match actually consumed a real EOF terminal — it is true
4604/// only on a successful match (or single-token deletion that lands on EOF), and
4605/// always false on single-token insertion, which synthesizes a missing token and
4606/// consumes nothing. Generated code feeds this into `finish_rule`'s
4607/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4608/// truly matched, matching ANTLR's `matchedEOF` semantics.
4609#[derive(Clone, Debug, Eq, PartialEq)]
4610pub struct GeneratedMatch {
4611    children: GeneratedMatchChildren,
4612    consumed_eof: bool,
4613}
4614
4615#[derive(Clone, Copy)]
4616enum GeneratedExpectedSymbols<'a> {
4617    Tree(&'a BTreeSet<i32>),
4618    TokenSet(ParserIntervalSet<'a>),
4619    TokenSetComplement {
4620        set: ParserIntervalSet<'a>,
4621        min_vocabulary: i32,
4622        max_vocabulary: i32,
4623    },
4624}
4625
4626impl GeneratedExpectedSymbols<'_> {
4627    fn is_empty(self) -> bool {
4628        match self {
4629            Self::Tree(symbols) => symbols.is_empty(),
4630            Self::TokenSet(set) => set.is_empty(),
4631            Self::TokenSetComplement {
4632                set,
4633                min_vocabulary,
4634                max_vocabulary,
4635            } => (min_vocabulary..=max_vocabulary).all(|symbol| set.contains(symbol)),
4636        }
4637    }
4638
4639    fn first(self) -> Option<i32> {
4640        match self {
4641            Self::Tree(symbols) => symbols.iter().next().copied(),
4642            Self::TokenSet(set) => set.ranges().next().map(|(start, _)| start),
4643            Self::TokenSetComplement {
4644                set,
4645                min_vocabulary,
4646                max_vocabulary,
4647            } => (min_vocabulary..=max_vocabulary).find(|symbol| !set.contains(*symbol)),
4648        }
4649    }
4650
4651    fn display(self, vocabulary: &Vocabulary) -> String {
4652        match self {
4653            Self::Tree(symbols) => expected_symbols_display(symbols, vocabulary),
4654            Self::TokenSet(set) => expected_symbols_display_iter(
4655                set.ranges().flat_map(|(start, stop)| start..=stop),
4656                vocabulary,
4657            ),
4658            Self::TokenSetComplement {
4659                set,
4660                min_vocabulary,
4661                max_vocabulary,
4662            } => expected_symbols_display_iter(
4663                (min_vocabulary..=max_vocabulary).filter(|symbol| !set.contains(*symbol)),
4664                vocabulary,
4665            ),
4666        }
4667    }
4668}
4669
4670#[derive(Clone, Debug, Eq, PartialEq)]
4671enum GeneratedMatchChildren {
4672    One(ParseTree),
4673    Many(Vec<ParseTree>),
4674}
4675
4676struct GeneratedMatchChildrenIntoIter {
4677    one: Option<ParseTree>,
4678    many: Option<std::vec::IntoIter<ParseTree>>,
4679}
4680
4681impl Iterator for GeneratedMatchChildrenIntoIter {
4682    type Item = ParseTree;
4683
4684    fn next(&mut self) -> Option<Self::Item> {
4685        self.one
4686            .take()
4687            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4688    }
4689}
4690
4691impl GeneratedMatch {
4692    /// Parse-tree children produced by the match (the matched terminal, an
4693    /// error node plus deleted-then-matched terminal, or a single missing-token
4694    /// error node).
4695    #[must_use]
4696    pub fn children(&self) -> &[ParseTree] {
4697        match &self.children {
4698            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4699            GeneratedMatchChildren::Many(children) => children,
4700        }
4701    }
4702
4703    /// Consumes the result, returning the children for appending to the rule
4704    /// context.
4705    #[must_use]
4706    pub fn into_children(self) -> Vec<ParseTree> {
4707        match self.children {
4708            GeneratedMatchChildren::One(child) => vec![child],
4709            GeneratedMatchChildren::Many(children) => children,
4710        }
4711    }
4712
4713    /// Consumes the match without allocating for the common single-child case.
4714    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4715        match self.children {
4716            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4717                one: Some(child),
4718                many: None,
4719            },
4720            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4721                one: None,
4722                many: Some(children.into_iter()),
4723            },
4724        }
4725    }
4726
4727    /// Whether a real EOF terminal was consumed by this match.
4728    #[must_use]
4729    pub const fn consumed_eof(&self) -> bool {
4730        self.consumed_eof
4731    }
4732}
4733
4734impl<S> BaseParser<S, NoSemanticHooks>
4735where
4736    S: TokenSource,
4737{
4738    /// Creates a parser base over a buffered token stream and recognizer
4739    /// metadata.
4740    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4741        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4742    }
4743}
4744
4745impl<S, H> BaseParser<S, H>
4746where
4747    S: TokenSource,
4748    H: SemanticHooks,
4749{
4750    /// Creates a parser base with caller-owned semantic hooks.
4751    pub fn with_semantic_hooks(
4752        input: CommonTokenStream<S>,
4753        data: RecognizerData,
4754        semantic_hooks: H,
4755    ) -> Self {
4756        Self {
4757            input,
4758            tree: ParseTreeStorage::new(),
4759            data,
4760            semantic_hooks,
4761            decision_override_generation: 0,
4762            build_parse_trees: true,
4763            syntax_errors: 0,
4764            report_diagnostic_errors: false,
4765            prediction_mode: PredictionMode::Ll,
4766            prediction_diagnostics: Vec::new(),
4767            reported_prediction_diagnostics: BTreeSet::new(),
4768            generated_parser_diagnostics: Vec::new(),
4769            generated_sync_expected: None,
4770            generated_recovery_error_index: None,
4771            generated_recovery_error_states: BTreeSet::new(),
4772            int_members: BTreeMap::new(),
4773            rule_context_stack: Vec::new(),
4774            rule_context_version: 0,
4775            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4776            pending_invoking_states: Vec::new(),
4777            precedence_stack: vec![0],
4778            invoked_predicates: Vec::new(),
4779            bail_on_error: false,
4780            unknown_predicate_policy: UnknownSemanticPolicy::default(),
4781            unknown_predicate_hits: Vec::new(),
4782            unhandled_action_hits: Vec::new(),
4783            rule_first_set_cache: Vec::new(),
4784            state_expected_cache: FxHashMap::default(),
4785            state_expected_token_cache: FxHashMap::default(),
4786            rule_stop_reach_cache: Vec::new(),
4787            recovery_symbols_intern: FxHashMap::default(),
4788            decision_lookahead_cache: FxHashMap::default(),
4789            ll1_decision_cache: FxHashMap::default(),
4790            fast_predicate_cache: FxHashMap::default(),
4791            empty_cycle_cache: Vec::new(),
4792            empty_cycle_cache_atn: None,
4793            clean_memo_mode: CleanMemoMode::Probe,
4794            clean_memo_probe_seen: FxHashSet::default(),
4795            clean_memo_probe_samples: 0,
4796            clean_memo_probe_repeats: 0,
4797            clean_memo_sparse_samples: 0,
4798            fast_recognize_scratch: FastRecognizeTopScratch::default(),
4799            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
4800            empty_recovery_symbols: Rc::new(BTreeSet::new()),
4801            fast_first_set_prefilter: true,
4802            fast_recovery_enabled: true,
4803            fast_token_nodes_enabled: true,
4804            fast_track_alt_numbers: false,
4805            recognition_arena: RecognitionArena::default(),
4806            last_recognition_arena_root: NodeSeqId::EMPTY,
4807            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
4808        }
4809    }
4810
4811    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
4812        &mut self.input
4813    }
4814
4815    /// Fully resets parser-owned state and rewinds the current token stream.
4816    ///
4817    /// Parser configuration, semantic hooks, learned DFA tables, and
4818    /// grammar-owned member values are retained.
4819    pub fn reset(&mut self) {
4820        self.input.seek(0);
4821        self.tree.reset();
4822        self.data.set_state(-1);
4823        self.syntax_errors = 0;
4824        self.prediction_diagnostics.clear();
4825        self.reported_prediction_diagnostics.clear();
4826        self.generated_parser_diagnostics.clear();
4827        self.generated_sync_expected = None;
4828        self.reset_generated_recovery_state();
4829        self.rule_context_stack.clear();
4830        self.advance_rule_context_version();
4831        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
4832        self.pending_invoking_states.clear();
4833        self.precedence_stack.clear();
4834        self.precedence_stack.push(0);
4835        self.invoked_predicates.clear();
4836        self.decision_override_generation = 0;
4837        self.unknown_predicate_hits.clear();
4838        self.unhandled_action_hits.clear();
4839        self.reset_per_parse_caches();
4840        self.fast_first_set_prefilter = true;
4841        self.fast_recovery_enabled = true;
4842        self.fast_token_nodes_enabled = self.build_parse_trees;
4843        self.fast_track_alt_numbers = false;
4844        self.reset_recognition_arena();
4845    }
4846
4847    /// Replaces the buffered token stream and fully resets this parser.
4848    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
4849        self.input = input;
4850        self.reset();
4851    }
4852
4853    /// Installs the policy for predicate coordinates that no translated table
4854    /// entry or user hook resolves.
4855    ///
4856    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
4857    /// but generated recursive-descent rules evaluate predicates directly
4858    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
4859    /// going through those options. Generated parser constructors call this so
4860    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
4861    /// the field at its `AssumeTrue` default and silently accepting an
4862    /// unimplemented hook predicate.
4863    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
4864        self.unknown_predicate_policy = policy;
4865    }
4866
4867    /// Reports any unknown predicate coordinate the generated-direct path
4868    /// recorded under [`UnknownSemanticPolicy::Error`], as an
4869    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
4870    /// after a rule completes so the fail-loud policy surfaces on the
4871    /// generated path the same way the interpreter entry surfaces it.
4872    #[must_use]
4873    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
4874        let error = self.unknown_semantic_error();
4875        self.unknown_predicate_hits.clear();
4876        self.unhandled_action_hits.clear();
4877        error
4878    }
4879
4880    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
4881    ///
4882    /// Generated parsers call this at the true top-level entry so a parser
4883    /// reused after a fail-loud (or recovered) parse starts clean, without
4884    /// clearing hits mid-parse where a generated parent still needs a child's
4885    /// recorded coordinate to survive to the top-level boundary.
4886    pub fn reset_unknown_semantic_hits(&mut self) {
4887        self.unknown_predicate_hits.clear();
4888        self.unhandled_action_hits.clear();
4889    }
4890
4891    /// Returns the token stream owned by this parser.
4892    #[must_use]
4893    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
4894        &self.input
4895    }
4896
4897    /// Returns the token stream for source replacement or in-place re-feeding.
4898    #[must_use]
4899    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
4900        &mut self.input
4901    }
4902
4903    /// Returns the canonical token store referenced by parse trees.
4904    #[must_use]
4905    pub const fn token_store(&self) -> &TokenStore {
4906        self.input.token_store()
4907    }
4908
4909    /// Returns the flat CST storage populated by completed rules.
4910    #[must_use]
4911    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
4912        &self.tree
4913    }
4914
4915    /// Resolves a compact parse-tree ID into a borrowing node view.
4916    #[must_use]
4917    pub fn node(&self, id: NodeId) -> Node<'_> {
4918        self.tree
4919            .node(self.input.token_store(), id)
4920            .expect("parser-produced node ID should remain valid")
4921    }
4922
4923    /// Consumes this parser and returns its token stream.
4924    #[must_use]
4925    pub fn into_token_stream(self) -> CommonTokenStream<S> {
4926        self.input
4927    }
4928
4929    /// Consumes this parser and returns its canonical token store.
4930    #[must_use]
4931    pub fn into_token_store(self) -> TokenStore {
4932        self.input.into_token_store()
4933    }
4934
4935    /// Consumes the parser and pairs its token store and flat CST with `root`.
4936    #[must_use]
4937    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
4938        ParsedFile::new(self.input.into_token_store(), self.tree, root)
4939    }
4940
4941    /// Returns the number of parser syntax errors recorded by committed parse
4942    /// paths so far.
4943    pub const fn number_of_syntax_errors(&self) -> usize {
4944        self.syntax_errors
4945    }
4946
4947    /// Computes reachability and retained-capacity counters for the most recent
4948    /// interpreted-rule recognition arena.
4949    ///
4950    /// The reachability scan is linear in the arena size and is deferred until
4951    /// this instrumentation method is called.
4952    #[must_use]
4953    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
4954        self.recognition_arena.stats(
4955            self.last_recognition_arena_root,
4956            self.last_recognition_arena_diagnostics,
4957        )
4958    }
4959
4960    /// Records a syntax error that generated parser code returns as fatal before
4961    /// it can recover into the current rule context.
4962    pub const fn record_generated_syntax_error(&mut self) {
4963        self.record_syntax_errors(1);
4964    }
4965
4966    const fn record_syntax_errors(&mut self, count: usize) {
4967        self.syntax_errors = self.syntax_errors.saturating_add(count);
4968    }
4969
4970    /// Emits diagnostics buffered by the token stream while generated parser
4971    /// code was fetching lexer tokens directly.
4972    pub fn report_token_source_errors(&mut self) {
4973        let errors = self.input.drain_source_errors();
4974        self.dispatch_token_source_errors(&errors);
4975    }
4976
4977    /// Captures generated-parser diagnostics and syntax-error count before a
4978    /// speculative generated rule path.
4979    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
4980        GeneratedDiagnosticsCheckpoint {
4981            diagnostics_len: self.generated_parser_diagnostics.len(),
4982            syntax_errors: self.syntax_errors,
4983            tree: self.tree.checkpoint(),
4984        }
4985    }
4986
4987    /// Restores generated-parser diagnostics after a speculative rule path failed.
4988    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
4989        self.generated_parser_diagnostics
4990            .truncate(marker.diagnostics_len);
4991        self.syntax_errors = marker.syntax_errors;
4992        self.generated_sync_expected = None;
4993        self.tree.rollback(marker.tree);
4994    }
4995
4996    /// Emits diagnostics recorded by committed generated parser recovery.
4997    pub fn report_generated_parser_diagnostics(&mut self) {
4998        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
4999        let token_errors = self.input.drain_source_errors();
5000        self.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
5001    }
5002
5003    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
5004        self.notify_error_listeners(
5005            diagnostic.line,
5006            diagnostic.column,
5007            &diagnostic.message,
5008            None,
5009        );
5010    }
5011
5012    fn dispatch_parser_diagnostics<'a>(
5013        &self,
5014        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
5015    ) {
5016        for diagnostic in diagnostics {
5017            self.dispatch_parser_diagnostic(diagnostic);
5018        }
5019    }
5020
5021    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
5022        if self.input.token_source().report_error(source_error) {
5023            return;
5024        }
5025        self.notify_error_listeners(
5026            source_error.line,
5027            source_error.column,
5028            &source_error.message,
5029            None,
5030        );
5031    }
5032
5033    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
5034        for error in errors {
5035            self.dispatch_token_source_error(error);
5036        }
5037    }
5038
5039    /// Dispatches generated parser and lexer diagnostics in the same
5040    /// source-position order as ANTLR's lazy token stream reports them.
5041    fn dispatch_generated_diagnostics(
5042        &self,
5043        parser_diagnostics: &[ParserDiagnostic],
5044        token_errors: &[TokenSourceError],
5045    ) {
5046        // Parser diagnostics keep their event order: Java's console and
5047        // DiagnosticErrorListener print reports as prediction produces them,
5048        // so reportAttemptingFullContext precedes reportContextSensitivity
5049        // even though the latter's position is earlier. Buffered token-source
5050        // errors interleave by source position and win ties.
5051        let mut token_iter = token_errors.iter().peekable();
5052        for diagnostic in parser_diagnostics {
5053            while let Some(error) = token_iter.peek() {
5054                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
5055                    self.dispatch_token_source_error(error);
5056                    token_iter.next();
5057                } else {
5058                    break;
5059                }
5060            }
5061            self.dispatch_parser_diagnostic(diagnostic);
5062        }
5063        for error in token_iter {
5064            self.dispatch_token_source_error(error);
5065        }
5066    }
5067
5068    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
5069    /// decision code.
5070    pub fn record_generated_ambiguity_diagnostic(
5071        &mut self,
5072        atn: &Atn,
5073        state_number: usize,
5074        start_index: usize,
5075        stop_index: usize,
5076        alts: &[usize],
5077    ) {
5078        if !self.report_diagnostic_errors || alts.len() < 2 {
5079            return;
5080        }
5081        let Some(decision) = atn
5082            .decision_to_state()
5083            .iter()
5084            .position(|candidate| candidate == state_number)
5085        else {
5086            return;
5087        };
5088        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5089            return;
5090        };
5091        let rule_name = self
5092            .rule_names()
5093            .get(rule_index)
5094            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5095        let input = display_input_text(&self.input.text(start_index, stop_index));
5096        let alts = alts
5097            .iter()
5098            .map(usize::to_string)
5099            .collect::<Vec<_>>()
5100            .join(", ");
5101        let key = (decision, start_index, format!("{alts}:{input}"));
5102        if !self.reported_prediction_diagnostics.insert(key) {
5103            return;
5104        }
5105        let start_diagnostic = diagnostic_for_token(
5106            self.token_at(start_index),
5107            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5108        );
5109        let stop_diagnostic = diagnostic_for_token(
5110            self.token_at(stop_index),
5111            format!(
5112                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5113            ),
5114        );
5115        self.generated_parser_diagnostics.push(start_diagnostic);
5116        self.generated_parser_diagnostics.push(stop_diagnostic);
5117    }
5118
5119    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5120    /// parser calls to the adaptive simulator.
5121    pub fn record_generated_prediction_diagnostic(
5122        &mut self,
5123        atn: &Atn,
5124        state_number: usize,
5125        prediction: &ParserAtnPrediction,
5126    ) {
5127        let Some(diagnostic) = &prediction.diagnostic else {
5128            return;
5129        };
5130        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5131            return;
5132        }
5133        let Some(decision) = atn
5134            .decision_to_state()
5135            .iter()
5136            .position(|candidate| candidate == state_number)
5137        else {
5138            return;
5139        };
5140        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5141            return;
5142        };
5143        let rule_name = self
5144            .rule_names()
5145            .get(rule_index)
5146            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5147        let attempt_input = display_input_text(
5148            &self
5149                .input
5150                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5151        );
5152        let result_input = display_input_text(
5153            &self
5154                .input
5155                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5156        );
5157        let alts = diagnostic
5158            .conflicting_alts
5159            .iter()
5160            .map(usize::to_string)
5161            .collect::<Vec<_>>()
5162            .join(", ");
5163        let key = (
5164            decision,
5165            diagnostic.start_index,
5166            format!(
5167                "{:?}:{alts}:{attempt_input}:{result_input}",
5168                diagnostic.kind
5169            ),
5170        );
5171        if !self.reported_prediction_diagnostics.insert(key) {
5172            return;
5173        }
5174        let attempt_diagnostic = diagnostic_for_token(
5175            self.token_at(diagnostic.sll_stop_index),
5176            format!(
5177                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5178            ),
5179        );
5180        self.generated_parser_diagnostics.push(attempt_diagnostic);
5181        let message = match diagnostic.kind {
5182            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5183                // Java's DiagnosticErrorListener is exactOnly by default:
5184                // non-exact ambiguities (default LL mode stopping at the
5185                // first resolvable conflict) report the attempt above but
5186                // suppress the ambiguity line itself.
5187                if !diagnostic.exact {
5188                    return;
5189                }
5190                format!(
5191                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5192                )
5193            }
5194            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5195                format!(
5196                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5197                )
5198            }
5199        };
5200        let result_diagnostic =
5201            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5202        self.generated_parser_diagnostics.push(result_diagnostic);
5203    }
5204
5205    pub fn la(&self, offset: isize) -> i32 {
5206        self.input.la_token(offset)
5207    }
5208
5209    pub fn consume(&mut self) {
5210        IntStream::consume(&mut self.input);
5211    }
5212
5213    /// Sets a generated integer member value used by target-template tests.
5214    pub fn set_int_member(&mut self, member: usize, value: i64) {
5215        self.int_members.insert(member, value);
5216    }
5217
5218    /// Reads a generated integer member value.
5219    pub fn int_member(&self, member: usize) -> Option<i64> {
5220        self.int_members.get(&member).copied()
5221    }
5222
5223    /// Captures generated integer members before speculative generated parser
5224    /// execution.
5225    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
5226        self.int_members.clone()
5227    }
5228
5229    /// Restores generated integer members after generated parser fallback.
5230    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
5231        self.int_members = members;
5232    }
5233
5234    /// Adds `delta` to a generated integer member and returns the new value.
5235    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5236        let value = self.int_members.entry(member).or_default();
5237        *value += delta;
5238        *value
5239    }
5240
5241    fn token_type_for_id(&self, id: TokenId) -> i32 {
5242        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5243    }
5244
5245    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5246        if self.build_parse_trees {
5247            self.tree.terminal(id)
5248        } else {
5249            NodeId::placeholder()
5250        }
5251    }
5252
5253    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5254        if self.build_parse_trees {
5255            self.tree.error(id)
5256        } else {
5257            NodeId::placeholder()
5258        }
5259    }
5260
5261    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5262        context.set_start_id(id);
5263    }
5264
5265    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5266        context.set_stop_id(id);
5267    }
5268
5269    fn insert_synthetic_token(
5270        &mut self,
5271        token_type: i32,
5272        text: String,
5273        line: usize,
5274        column: usize,
5275    ) -> Result<TokenId, AntlrError> {
5276        self.input
5277            .insert(
5278                TokenSpec::explicit(token_type, text)
5279                    .with_span(usize::MAX, usize::MAX)
5280                    .with_byte_span(0, 0)
5281                    .with_position(line, column),
5282            )
5283            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5284    }
5285
5286    /// Matches and consumes the current token when it has the expected token
5287    /// type.
5288    ///
5289    /// On success the consumed token is wrapped as a terminal parse-tree node.
5290    /// On mismatch the error carries vocabulary display names so diagnostics are
5291    /// stable across literal and symbolic token naming.
5292    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5293        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5294            line: 0,
5295            column: 0,
5296            message: "missing current token".to_owned(),
5297        })?;
5298        let current_type = self.token_type_for_id(current);
5299        if current_type == token_type {
5300            self.reset_generated_recovery_state();
5301            self.consume();
5302            Ok(self.terminal_tree(current))
5303        } else {
5304            Err(AntlrError::MismatchedInput {
5305                expected: self.vocabulary().display_name(token_type),
5306                found: self.vocabulary().display_name(current_type),
5307            })
5308        }
5309    }
5310
5311    /// Matches a token from generated recursive-descent code, including ANTLR's
5312    /// single-token insertion recovery when the active rule context can legally
5313    /// continue at the current input symbol.
5314    pub fn match_token_recovering(
5315        &mut self,
5316        token_type: i32,
5317        follow_state: usize,
5318        atn: &Atn,
5319    ) -> Result<GeneratedMatch, AntlrError> {
5320        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5321            line: 0,
5322            column: 0,
5323            message: "missing current token".to_owned(),
5324        })?;
5325        let current_type = self.token_type_for_id(current);
5326        if current_type == token_type {
5327            self.generated_sync_expected = None;
5328            self.reset_generated_recovery_state();
5329            let consumed_eof = current_type == TOKEN_EOF;
5330            self.consume();
5331            return Ok(GeneratedMatch {
5332                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5333                consumed_eof,
5334            });
5335        }
5336        let mut expected_symbols = BTreeSet::new();
5337        expected_symbols.insert(token_type);
5338        self.recover_generated_match(
5339            current,
5340            GeneratedExpectedSymbols::Tree(&expected_symbols),
5341            follow_state,
5342            atn,
5343            |symbol| symbol == token_type,
5344        )
5345    }
5346
5347    pub fn match_set_recovering(
5348        &mut self,
5349        intervals: &[(i32, i32)],
5350        follow_state: usize,
5351        atn: &Atn,
5352    ) -> Result<GeneratedMatch, AntlrError> {
5353        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5354            line: 0,
5355            column: 0,
5356            message: "missing current token".to_owned(),
5357        })?;
5358        let current_type = self.token_type_for_id(current);
5359        if interval_set_contains(intervals, current_type) {
5360            self.generated_sync_expected = None;
5361            self.reset_generated_recovery_state();
5362            let consumed_eof = current_type == TOKEN_EOF;
5363            self.consume();
5364            return Ok(GeneratedMatch {
5365                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5366                consumed_eof,
5367            });
5368        }
5369        let expected_symbols = interval_symbols(intervals);
5370        self.recover_generated_match(
5371            current,
5372            GeneratedExpectedSymbols::Tree(&expected_symbols),
5373            follow_state,
5374            atn,
5375            |symbol| interval_set_contains(intervals, symbol),
5376        )
5377    }
5378
5379    pub fn match_token_set_recovering(
5380        &mut self,
5381        set: ParserIntervalSet<'_>,
5382        follow_state: usize,
5383        atn: &Atn,
5384    ) -> Result<GeneratedMatch, AntlrError> {
5385        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5386            line: 0,
5387            column: 0,
5388            message: "missing current token".to_owned(),
5389        })?;
5390        let current_type = self.token_type_for_id(current);
5391        if set.contains(current_type) {
5392            self.generated_sync_expected = None;
5393            self.reset_generated_recovery_state();
5394            let consumed_eof = current_type == TOKEN_EOF;
5395            self.consume();
5396            return Ok(GeneratedMatch {
5397                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5398                consumed_eof,
5399            });
5400        }
5401        self.recover_generated_match(
5402            current,
5403            GeneratedExpectedSymbols::TokenSet(set),
5404            follow_state,
5405            atn,
5406            |symbol| set.contains(symbol),
5407        )
5408    }
5409
5410    pub fn match_not_set_recovering(
5411        &mut self,
5412        intervals: &[(i32, i32)],
5413        min_vocabulary: i32,
5414        max_vocabulary: i32,
5415        follow_state: usize,
5416        atn: &Atn,
5417    ) -> Result<GeneratedMatch, AntlrError> {
5418        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5419            line: 0,
5420            column: 0,
5421            message: "missing current token".to_owned(),
5422        })?;
5423        let current_type = self.token_type_for_id(current);
5424        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5425            && !interval_set_contains(intervals, current_type)
5426        {
5427            self.generated_sync_expected = None;
5428            self.reset_generated_recovery_state();
5429            let consumed_eof = current_type == TOKEN_EOF;
5430            self.consume();
5431            return Ok(GeneratedMatch {
5432                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5433                consumed_eof,
5434            });
5435        }
5436        let expected_symbols =
5437            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5438        self.recover_generated_match(
5439            current,
5440            GeneratedExpectedSymbols::Tree(&expected_symbols),
5441            follow_state,
5442            atn,
5443            |symbol| {
5444                (min_vocabulary..=max_vocabulary).contains(&symbol)
5445                    && !interval_set_contains(intervals, symbol)
5446            },
5447        )
5448    }
5449
5450    pub fn match_not_token_set_recovering(
5451        &mut self,
5452        set: ParserIntervalSet<'_>,
5453        min_vocabulary: i32,
5454        max_vocabulary: i32,
5455        follow_state: usize,
5456        atn: &Atn,
5457    ) -> Result<GeneratedMatch, AntlrError> {
5458        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5459            line: 0,
5460            column: 0,
5461            message: "missing current token".to_owned(),
5462        })?;
5463        let current_type = self.token_type_for_id(current);
5464        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
5465        {
5466            self.generated_sync_expected = None;
5467            self.reset_generated_recovery_state();
5468            let consumed_eof = current_type == TOKEN_EOF;
5469            self.consume();
5470            return Ok(GeneratedMatch {
5471                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5472                consumed_eof,
5473            });
5474        }
5475        self.recover_generated_match(
5476            current,
5477            GeneratedExpectedSymbols::TokenSetComplement {
5478                set,
5479                min_vocabulary,
5480                max_vocabulary,
5481            },
5482            follow_state,
5483            atn,
5484            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
5485        )
5486    }
5487
5488    fn recover_generated_match(
5489        &mut self,
5490        current: TokenId,
5491        expected_symbols: GeneratedExpectedSymbols<'_>,
5492        follow_state: usize,
5493        atn: &Atn,
5494        matches: impl Fn(i32) -> bool,
5495    ) -> Result<GeneratedMatch, AntlrError> {
5496        let expected_display = expected_symbols.display(self.vocabulary());
5497        let (current_type, current_line, current_column, current_display) = {
5498            let token = self
5499                .input
5500                .token_view(current)
5501                .expect("current token ID should be valid");
5502            (
5503                token.token_type(),
5504                token.line(),
5505                token.column(),
5506                token_input_display(&token),
5507            )
5508        };
5509        if self.bail_on_error {
5510            return Err(AntlrError::ParserError {
5511                line: current_line,
5512                column: current_column,
5513                message: format!("mismatched input {current_display} expecting {expected_display}"),
5514            });
5515        }
5516        if current_type != TOKEN_EOF
5517            && let Some(next) = self.input.lt_id(2)
5518            && matches(self.token_type_for_id(next))
5519        {
5520            let message =
5521                format!("extraneous input {current_display} expecting {expected_display}");
5522            self.push_generated_parser_diagnostic(ParserDiagnostic {
5523                line: current_line,
5524                column: current_column,
5525                message,
5526            });
5527            self.record_syntax_errors(1);
5528            self.generated_sync_expected = None;
5529            // Single-token deletion: skip `current`, then accept `next`. The
5530            // accepted token can be EOF only if it is a real EOF terminal.
5531            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5532            self.consume();
5533            self.consume();
5534            self.reset_generated_recovery_state();
5535            return Ok(GeneratedMatch {
5536                children: GeneratedMatchChildren::Many(vec![
5537                    self.error_tree(current),
5538                    self.terminal_tree(next),
5539                ]),
5540                consumed_eof,
5541            });
5542        }
5543        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5544        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5545        // *after* the current element can consume the current symbol. At EOF that
5546        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5547        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5548        // when EOF merely leaks in from the empty enclosing context (as in
5549        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5550        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5551        // so consult the follow state's OWN expected set for the explicit case.
5552        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5553            && self
5554                .cached_state_expected_symbols(atn, follow_state)
5555                .contains(&TOKEN_EOF);
5556        if follow_symbols.contains(&current_type)
5557            && (current_type != TOKEN_EOF
5558                || self.rule_context_stack.len() > 1
5559                || expected_symbols.is_empty()
5560                || follow_explicitly_expects_eof)
5561        {
5562            let message = format!("missing {expected_display} at {current_display}");
5563            self.push_generated_parser_diagnostic(ParserDiagnostic {
5564                line: current_line,
5565                column: current_column,
5566                message,
5567            });
5568            self.record_syntax_errors(1);
5569            self.generated_sync_expected = None;
5570            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
5571            let missing_display = expected_symbol_display(token_type, self.vocabulary());
5572            let token = self.insert_synthetic_token(
5573                token_type,
5574                format!("<missing {missing_display}>"),
5575                current_line,
5576                current_column,
5577            )?;
5578            // Single-token insertion synthesizes a missing token and consumes
5579            // nothing, so no EOF terminal is consumed even when the lookahead is
5580            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5581            // from recording EOF as the rule stop on this recovery path.
5582            return Ok(GeneratedMatch {
5583                children: GeneratedMatchChildren::One(self.error_tree(token)),
5584                consumed_eof: false,
5585            });
5586        }
5587        let mismatch_expected_display = self
5588            .generated_sync_expected
5589            .take()
5590            .map_or(expected_display, |symbols| {
5591                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
5592            });
5593        Err(AntlrError::ParserError {
5594            line: current_line,
5595            column: current_column,
5596            message: format!(
5597                "mismatched input {current_display} expecting {mismatch_expected_display}"
5598            ),
5599        })
5600    }
5601
5602    fn generated_recovery_follow_symbols(
5603        &mut self,
5604        atn: &Atn,
5605        follow_state: usize,
5606    ) -> BTreeSet<i32> {
5607        let mut follow = self
5608            .cached_state_expected_symbols(atn, follow_state)
5609            .as_ref()
5610            .clone();
5611        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5612            follow.extend(self.context_expected_symbols(atn));
5613        }
5614        follow
5615    }
5616
5617    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5618        self.match_token(TOKEN_EOF)
5619    }
5620
5621    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5622        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5623    }
5624
5625    pub fn match_not_set(
5626        &mut self,
5627        intervals: &[(i32, i32)],
5628        min_vocabulary: i32,
5629        max_vocabulary: i32,
5630    ) -> Result<ParseTree, AntlrError> {
5631        self.match_interval_condition(intervals, |symbol| {
5632            (min_vocabulary..=max_vocabulary).contains(&symbol)
5633                && !interval_set_contains(intervals, symbol)
5634        })
5635    }
5636
5637    fn match_interval_condition(
5638        &mut self,
5639        intervals: &[(i32, i32)],
5640        matches: impl FnOnce(i32) -> bool,
5641    ) -> Result<ParseTree, AntlrError> {
5642        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5643            line: 0,
5644            column: 0,
5645            message: "missing current token".to_owned(),
5646        })?;
5647        let current_type = self.token_type_for_id(current);
5648        if matches(current_type) {
5649            self.reset_generated_recovery_state();
5650            self.consume();
5651            Ok(self.terminal_tree(current))
5652        } else {
5653            Err(AntlrError::MismatchedInput {
5654                expected: self.interval_display(intervals),
5655                found: self.vocabulary().display_name(current_type),
5656            })
5657        }
5658    }
5659
5660    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5661        let values = intervals
5662            .iter()
5663            .map(|(start, stop)| {
5664                if start == stop {
5665                    self.vocabulary().display_name(*start)
5666                } else {
5667                    format!(
5668                        "{}..{}",
5669                        self.vocabulary().display_name(*start),
5670                        self.vocabulary().display_name(*stop)
5671                    )
5672                }
5673            })
5674            .collect::<Vec<_>>()
5675            .join(", ");
5676        format!("{{{values}}}")
5677    }
5678
5679    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5680        if self.build_parse_trees {
5681            self.tree.finish_rule(context)
5682        } else {
5683            NodeId::placeholder()
5684        }
5685    }
5686
5687    /// Enters a generated parser rule and returns the context object the
5688    /// generated method should populate.
5689    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
5690        self.set_state(state);
5691        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
5692        self.rule_context_stack.push(RuleContextFrame {
5693            rule_index,
5694            invoking_state,
5695        });
5696        self.advance_rule_context_version();
5697        let start_index = self.current_visible_index();
5698        let mut context = ParserRuleContext::new(rule_index, invoking_state);
5699        if let Some(token) = self.token_id_at(start_index) {
5700            self.set_context_start(&mut context, token);
5701        }
5702        context
5703    }
5704
5705    /// Records the ATN source state for the next generated rule invocation.
5706    ///
5707    /// ANTLR's full-context prediction reconstructs caller follow states from
5708    /// each active rule context's invoking state. Generated Rust rule methods are
5709    /// plain functions, so the caller supplies that ATN state just before making a
5710    /// rule call; `enter_rule` consumes it when the callee starts.
5711    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
5712        let marker = self.pending_invoking_states.len();
5713        self.pending_invoking_states.push(invoking_state);
5714        marker
5715    }
5716
5717    /// Discards an invoking-state marker if the callee did not consume it.
5718    pub fn discard_invoking_state(&mut self, marker: usize) {
5719        self.pending_invoking_states.truncate(marker);
5720    }
5721
5722    /// Exits the current generated parser rule.
5723    pub fn exit_rule(&mut self) {
5724        self.rule_context_stack.pop();
5725        self.advance_rule_context_version();
5726    }
5727
5728    /// Returns caller follow states for interning in a parser ATN simulator's
5729    /// prediction store. States are yielded outermost to innermost.
5730    pub fn prediction_context_return_states<'a>(
5731        &'a self,
5732        atn: &'a Atn,
5733    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
5734        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
5735            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
5736                return None;
5737            };
5738            let Some(Transition::Rule { follow_state, .. }) = atn
5739                .state(state_number)
5740                .and_then(|state| state.transitions().first())
5741                .map(ParserTransition::data)
5742            else {
5743                return None;
5744            };
5745            Some(follow_state)
5746        })
5747    }
5748
5749    /// Returns a generation that changes whenever the active rule stack changes.
5750    ///
5751    /// A parser ATN simulator uses this to reuse an interned outer prediction
5752    /// context while generated predictions remain in the same rule context.
5753    pub const fn rule_context_version(&self) -> usize {
5754        self.rule_context_version
5755    }
5756
5757    const fn advance_rule_context_version(&mut self) {
5758        self.rule_context_version = self.rule_context_version.wrapping_add(1);
5759    }
5760
5761    /// Adds a generated parser child only when parse-tree construction is
5762    /// enabled. The match is recorded on the context either way (via `add_child`,
5763    /// or `note_matched_child` when trees are off) so generated recovery can tell
5764    /// whether the rule has matched anything yet without depending on `children`.
5765    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
5766        if self.build_parse_trees {
5767            self.tree.add_child(context, child);
5768        } else {
5769            context.note_matched_child();
5770        }
5771    }
5772
5773    fn release_tree_scratch_if_idle(&mut self) {
5774        if self.rule_context_stack.is_empty() {
5775            self.tree.release_scratch();
5776        }
5777    }
5778
5779    /// Finishes a generated parser rule and returns its parse-tree node.
5780    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
5781        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5782        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5783            self.set_context_stop(&mut context, token);
5784        }
5785        let node = self.rule_node(context);
5786        self.exit_rule();
5787        self.release_tree_scratch_if_idle();
5788        node
5789    }
5790
5791    /// Recovers a generated rule catch block after a committed mismatch.
5792    ///
5793    /// ANTLR's generated parsers catch recognition errors inside each rule,
5794    /// report the original error, then consume unexpected tokens until the
5795    /// caller's recovery set can resume. Tokens consumed during recovery become
5796    /// error nodes in the current rule context.
5797    pub fn recover_generated_rule(
5798        &mut self,
5799        context: &mut ParserRuleContext,
5800        atn: &Atn,
5801        error: AntlrError,
5802    ) {
5803        let diagnostic = self.generated_rule_error_diagnostic(error);
5804        self.push_generated_parser_diagnostic(diagnostic);
5805        self.generated_sync_expected = None;
5806        let error_index = self.input.index();
5807        let error_state = self.data.state();
5808        // Match ANTLR's lastErrorIndex/lastErrorStates failsafe: a recovery
5809        // token can also be in the caller's follow set, leaving the cursor
5810        // unchanged and allowing generated outer decisions to revisit the same
5811        // failed state forever.
5812        if self.generated_recovery_error_index == Some(error_index)
5813            && self.generated_recovery_error_states.contains(&error_state)
5814            && self.la(1) != TOKEN_EOF
5815            && let Some(token) = self.input.lt_id(1)
5816        {
5817            self.consume();
5818            let child = self.error_tree(token);
5819            self.add_parse_child(context, child);
5820        }
5821        let recovery_index = self.input.index();
5822        if self.generated_recovery_error_index != Some(recovery_index) {
5823            self.generated_recovery_error_index = Some(recovery_index);
5824            self.generated_recovery_error_states.clear();
5825        }
5826        self.generated_recovery_error_states.insert(error_state);
5827        let recovery_symbols = self.context_expected_symbols(atn);
5828        loop {
5829            let symbol = self.la(1);
5830            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
5831                break;
5832            }
5833            let Some(token) = self.input.lt_id(1) else {
5834                break;
5835            };
5836            self.consume();
5837            let child = self.error_tree(token);
5838            self.add_parse_child(context, child);
5839        }
5840        self.record_syntax_errors(1);
5841    }
5842
5843    fn reset_generated_recovery_state(&mut self) {
5844        if self.generated_recovery_error_index.is_some() {
5845            self.generated_recovery_error_index = None;
5846            self.generated_recovery_error_states.clear();
5847        }
5848    }
5849
5850    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
5851        if self
5852            .generated_parser_diagnostics
5853            .iter()
5854            .any(|existing| existing == &diagnostic)
5855        {
5856            return;
5857        }
5858        self.generated_parser_diagnostics.push(diagnostic);
5859    }
5860
5861    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
5862        match error {
5863            AntlrError::ParserError {
5864                line,
5865                column,
5866                message,
5867            } => ParserDiagnostic {
5868                line,
5869                column,
5870                message,
5871            },
5872            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
5873                self.input.lt(1),
5874                format!("mismatched input {found} expecting {expected}"),
5875            ),
5876            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
5877                self.input.lt(1),
5878                format!("no viable alternative at input {input}"),
5879            ),
5880            AntlrError::LexerError {
5881                line,
5882                column,
5883                message,
5884            } => ParserDiagnostic {
5885                line,
5886                column,
5887                message,
5888            },
5889            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
5890        }
5891    }
5892
5893    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
5894    pub fn finish_recursion_rule(
5895        &mut self,
5896        mut context: ParserRuleContext,
5897        consumed_eof: bool,
5898    ) -> ParseTree {
5899        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5900        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5901            self.set_context_stop(&mut context, token);
5902        }
5903        let node = self.rule_node(context);
5904        self.unroll_recursion_context();
5905        self.release_tree_scratch_if_idle();
5906        node
5907    }
5908
5909    /// Enters a generated left-recursive rule at `precedence`.
5910    pub fn enter_recursion_rule(
5911        &mut self,
5912        state: isize,
5913        rule_index: usize,
5914        precedence: i32,
5915    ) -> ParserRuleContext {
5916        self.precedence_stack.push(precedence);
5917        self.enter_rule(state, rule_index)
5918    }
5919
5920    /// Replaces the current context while expanding a left-recursive rule.
5921    pub fn push_new_recursion_context(
5922        &mut self,
5923        state: isize,
5924        rule_index: usize,
5925    ) -> ParserRuleContext {
5926        self.set_state(state);
5927        ParserRuleContext::new(rule_index, state)
5928    }
5929
5930    /// Wraps the previous left-recursive context before parsing the next
5931    /// recursive operator alternative.
5932    pub fn push_new_recursion_context_with_previous(
5933        &mut self,
5934        state: isize,
5935        rule_index: usize,
5936        current: &mut ParserRuleContext,
5937    ) {
5938        self.set_state(state);
5939        if let Some(stop) = self
5940            .rule_stop_token_index(self.input.index(), false)
5941            .and_then(|index| self.token_id_at(index))
5942        {
5943            self.set_context_stop(current, stop);
5944        }
5945        let invoking_state = current.invoking_state();
5946        let start = current.start_id();
5947        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
5948        if start.is_some() {
5949            replacement.set_start_from_context(current);
5950        }
5951        let previous = std::mem::replace(current, replacement);
5952        if self.build_parse_trees {
5953            let previous = self.rule_node(previous);
5954            self.tree.add_child(current, previous);
5955        }
5956    }
5957
5958    /// Leaves a generated left-recursive rule.
5959    pub fn unroll_recursion_context(&mut self) {
5960        if self.precedence_stack.len() > 1 {
5961            self.precedence_stack.pop();
5962        }
5963        self.exit_rule();
5964    }
5965
5966    /// Predicts a generated left-recursive loop from one-token lookahead.
5967    ///
5968    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
5969    /// `None` means caller overlap, a dangerous multi-token prefix, or an
5970    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
5971    /// prediction (which includes the exit alt and precedence filtering).
5972    ///
5973    /// Single-token operators and multi-token prefixes that do not shadow a
5974    /// lower-precedence single-token operator keep the one-token enter fast path.
5975    ///
5976    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
5977    /// operator must not force enter; the adaptive decision may need to select
5978    /// the loop exit instead.
5979    pub fn left_recursive_loop_enter_prediction(
5980        &mut self,
5981        atn: &Atn,
5982        state_number: usize,
5983        precedence: i32,
5984    ) -> Option<bool> {
5985        let symbol = self.la(1);
5986        if symbol == TOKEN_EOF {
5987            return Some(false);
5988        }
5989        let operator_lookahead =
5990            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
5991        let can_single = operator_lookahead.single_token.contains(symbol);
5992        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
5993        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
5994        if !can_single && !can_multi && !can_predicate {
5995            return Some(false);
5996        }
5997        if can_predicate && !can_single {
5998            return None;
5999        }
6000        // Multi-token-only at this precedence, but the same symbol is a
6001        // single-token operator at precedence 0: defer so exit can win when the
6002        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
6003        if !can_single && can_multi && precedence > 0 {
6004            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
6005            if baseline.single_token.contains(symbol) {
6006                return None;
6007            }
6008        }
6009        let atn_key = SharedAtnCacheKey::for_atn(atn);
6010        let cached_overlap = self
6011            .left_recursive_caller_overlap_cache
6012            .iter()
6013            .flatten()
6014            .find(|entry| {
6015                entry.atn_key == atn_key
6016                    && entry.state_number == state_number
6017                    && entry.symbol == symbol
6018                    && entry.context_version == self.rule_context_version
6019            })
6020            .map(|entry| entry.overlaps);
6021        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
6022            let overlaps = caller_context_can_match_symbol_before_state(
6023                atn,
6024                self.prediction_context_return_states(atn),
6025                state_number,
6026                symbol,
6027            );
6028            if let Some(slot) = self
6029                .left_recursive_caller_overlap_cache
6030                .iter_mut()
6031                .find(|slot| slot.is_none())
6032            {
6033                *slot = Some(LeftRecursiveCallerOverlap {
6034                    atn_key,
6035                    state_number,
6036                    symbol,
6037                    context_version: self.rule_context_version,
6038                    overlaps,
6039                });
6040            }
6041            overlaps
6042        });
6043        if caller_overlaps {
6044            return None;
6045        }
6046        Some(true)
6047    }
6048
6049    fn cached_left_recursive_operator_lookahead(
6050        atn: &Atn,
6051        state_number: usize,
6052        precedence: i32,
6053    ) -> Rc<LeftRecursiveOperatorLookahead> {
6054        with_shared_atn_caches(atn, |cache| {
6055            let key = (state_number, precedence);
6056            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
6057                return Rc::clone(cached);
6058            }
6059            let lookahead = Rc::new(left_recursive_operator_lookahead(
6060                atn,
6061                state_number,
6062                precedence,
6063            ));
6064            cache
6065                .left_recursive_operator_lookahead
6066                .insert(key, Rc::clone(&lookahead));
6067            lookahead
6068        })
6069    }
6070
6071    /// Checks whether a generated left-recursive loop can unambiguously enter
6072    /// its operator alternative from one-token lookahead.
6073    pub fn left_recursive_loop_enter_matches(
6074        &mut self,
6075        atn: &Atn,
6076        state_number: usize,
6077        precedence: i32,
6078    ) -> bool {
6079        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
6080    }
6081
6082    /// Implements generated `precpred(_ctx, k)` checks.
6083    pub fn precpred(&self, precedence: i32) -> bool {
6084        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
6085    }
6086
6087    /// Evaluates a generated parser semantic predicate at the current input
6088    /// position.
6089    pub fn parser_semantic_predicate_matches(
6090        &mut self,
6091        predicates: &[(usize, usize, ParserPredicate)],
6092        rule_index: usize,
6093        pred_index: usize,
6094    ) -> bool {
6095        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
6096    }
6097
6098    /// Evaluates a generated parser semantic predicate with the current integer
6099    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
6100    pub fn parser_semantic_predicate_matches_with_local(
6101        &mut self,
6102        predicates: &[(usize, usize, ParserPredicate)],
6103        rule_index: usize,
6104        pred_index: usize,
6105        local_int_arg: i32,
6106    ) -> bool {
6107        self.parser_semantic_predicate_matches_inner(
6108            predicates,
6109            rule_index,
6110            pred_index,
6111            Some((rule_index, i64::from(local_int_arg))),
6112        )
6113    }
6114
6115    fn parser_semantic_predicate_matches_inner(
6116        &mut self,
6117        predicates: &[(usize, usize, ParserPredicate)],
6118        rule_index: usize,
6119        pred_index: usize,
6120        local_int_arg: Option<(usize, i64)>,
6121    ) -> bool {
6122        let index = self.input.index();
6123        let member_values = self.int_members.clone();
6124        self.parser_predicate_matches(PredicateEval {
6125            index,
6126            rule_index,
6127            pred_index,
6128            predicates,
6129            semantics: None,
6130            context: None,
6131            local_int_arg,
6132            member_values: &member_values,
6133        })
6134    }
6135
6136    /// Evaluates a generated parser semantic predicate with access to the
6137    /// current generated rule context.
6138    pub fn parser_semantic_predicate_matches_with_context_and_local(
6139        &mut self,
6140        predicates: &[(usize, usize, ParserPredicate)],
6141        rule_index: usize,
6142        pred_index: usize,
6143        context: &ParserRuleContext,
6144        local_int_arg: i32,
6145    ) -> bool {
6146        let index = self.input.index();
6147        let member_values = self.int_members.clone();
6148        self.parser_predicate_matches(PredicateEval {
6149            index,
6150            rule_index,
6151            pred_index,
6152            predicates,
6153            semantics: None,
6154            context: Some(context),
6155            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6156            member_values: &member_values,
6157        })
6158    }
6159
6160    /// Evaluates a generated `SemIR` parser predicate with access to the current
6161    /// generated rule context.
6162    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
6163        &mut self,
6164        semantics: &ParserSemantics,
6165        rule_index: usize,
6166        pred_index: usize,
6167        context: &ParserRuleContext,
6168        local_int_arg: i32,
6169    ) -> bool {
6170        let index = self.input.index();
6171        let member_values = self.int_members.clone();
6172        self.parser_predicate_matches(PredicateEval {
6173            index,
6174            rule_index,
6175            pred_index,
6176            predicates: &[],
6177            semantics: Some(semantics),
6178            context: Some(context),
6179            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6180            member_values: &member_values,
6181        })
6182    }
6183
6184    /// Returns a generated fail-option message for a parser semantic
6185    /// predicate coordinate.
6186    pub fn parser_semantic_predicate_failure_message(
6187        &self,
6188        rule_index: usize,
6189        pred_index: usize,
6190        predicates: &[(usize, usize, ParserPredicate)],
6191    ) -> Option<&'static str> {
6192        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
6193    }
6194
6195    /// Matches any non-EOF token.
6196    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
6197        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6198            line: 0,
6199            column: 0,
6200            message: "missing current token".to_owned(),
6201        })?;
6202        if self.token_type_for_id(current) == TOKEN_EOF {
6203            return Err(AntlrError::MismatchedInput {
6204                expected: "wildcard".to_owned(),
6205                found: self.vocabulary().display_name(TOKEN_EOF),
6206            });
6207        }
6208        self.reset_generated_recovery_state();
6209        self.consume();
6210        Ok(self.terminal_tree(current))
6211    }
6212
6213    /// Generated parser synchronization hook. The current interpreter owns
6214    /// recovery; direct generated methods can call this as a no-op until the
6215    /// generated recovery strategy is expanded.
6216    #[allow(clippy::unnecessary_wraps)]
6217    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
6218        self.set_state(state);
6219        Ok(())
6220    }
6221
6222    /// Synchronizes a generated parser decision against the ATN lookahead set.
6223    ///
6224    /// ANTLR generated parsers call the error strategy before optional and loop
6225    /// decisions. When the current token cannot start any alternative, follow a
6226    /// nullable exit, or be deleted before a later synchronization token, the
6227    /// generated Rust method reports that decision-level mismatch instead of
6228    /// descending into a child rule that cannot start at the current token.
6229    pub fn sync_decision(
6230        &mut self,
6231        atn: &Atn,
6232        state_number: usize,
6233        current_context_empty: bool,
6234        loop_back: bool,
6235    ) -> Result<Vec<ParseTree>, AntlrError> {
6236        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
6237        self.generated_sync_expected = None;
6238        let Some(state) = atn.state(state_number) else {
6239            return Ok(Vec::new());
6240        };
6241        let Some(rule_index) = state.rule_index() else {
6242            return Ok(Vec::new());
6243        };
6244        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
6245            return Ok(Vec::new());
6246        };
6247        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6248        let symbol = self.la(1);
6249        let mut has_expected_symbols = false;
6250        let mut nullable = false;
6251        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
6252        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
6253        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
6254        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
6255        // and worth deleting; an implicit-follow EOF means the loop should simply
6256        // exit and leave the token for the (absent) caller — matching ANTLR, which
6257        // exits the loop via prediction rather than consuming up to a synthetic EOF.
6258        let mut explicit_eof_expected = false;
6259        for transition in &entry.transitions {
6260            if transition.symbols.contains(symbol) {
6261                return Ok(Vec::new());
6262            }
6263            has_expected_symbols |= !transition.symbols.is_empty();
6264            nullable |= transition.nullable;
6265            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
6266        }
6267        // Happy path: a nullable decision exits when the symbol is in the
6268        // rule-stack follow set. Answer the membership question with an
6269        // early-exit walk; the full union below is only needed for the
6270        // mismatch/deletion diagnostics.
6271        if nullable && self.context_expected_contains(atn, symbol) {
6272            return Ok(Vec::new());
6273        }
6274        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
6275        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
6276            return Ok(Vec::new());
6277        }
6278        let mut expected = TokenBitSet::default();
6279        for transition in &entry.transitions {
6280            expected.extend_from(&transition.symbols);
6281        }
6282        if let Some(context_expected) = context_expected {
6283            expected.extend_from(&context_expected);
6284        }
6285        let can_delete_in_place =
6286            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
6287        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
6288        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
6289        // least one iteration) does `consumeUntil` the follow set — multi-token
6290        // deletion, one error per skipped token across iterations; a loop ENTRY
6291        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
6292        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
6293        // unexpected token only when LA(2) is expected, otherwise report a mismatch
6294        // and leave recovery to the rule.
6295        //
6296        // The generated loop always presents the loop-ENTRY state to this method on
6297        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
6298        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
6299        // an iteration has been taken, and true on a `+` loop's first sync since its
6300        // mandatory first element is iteration 1). Treating a loop entry as a
6301        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
6302        // `c`s, which ANTLR rejects with `mismatched input`).
6303        let loop_sync = loop_back;
6304        if symbol != TOKEN_EOF && can_delete_in_place {
6305            let mut cursor = self.input.index();
6306            let mut skipped = Vec::new();
6307            loop {
6308                let current = self.token_type_at(cursor);
6309                if current == TOKEN_EOF {
6310                    break;
6311                }
6312                skipped.push(cursor);
6313                let next = self.consume_index(cursor, current);
6314                if next == cursor {
6315                    break;
6316                }
6317                let next_symbol = self.token_type_at(next);
6318                // Stop (and delete the skipped tokens as error nodes) when the next
6319                // token is a real expected continuation. EOF counts only when it is
6320                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
6321                // genuinely extraneous and the generated EOF match consumes the real
6322                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
6323                // rule-follow) does NOT count — the loop must exit and leave the
6324                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
6325                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
6326                    explicit_eof_expected
6327                } else {
6328                    expected.contains(next_symbol)
6329                };
6330                if next_is_expected_stop {
6331                    let current_token = self.input.lt(1);
6332                    let expected_symbols = expected.to_btree_set();
6333                    let message = format!(
6334                        "extraneous input {} expecting {}",
6335                        current_token
6336                            .as_ref()
6337                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6338                        self.expected_symbols_display(&expected_symbols)
6339                    );
6340                    self.push_generated_parser_diagnostic(diagnostic_for_token(
6341                        current_token,
6342                        message,
6343                    ));
6344                    self.record_syntax_errors(1);
6345                    let mut children = Vec::with_capacity(skipped.len());
6346                    for index in skipped {
6347                        if let Some(token) = self.token_id_at(index) {
6348                            self.consume();
6349                            children.push(self.error_tree(token));
6350                        }
6351                    }
6352                    if !loop_sync {
6353                        self.reset_generated_recovery_state();
6354                    }
6355                    return Ok(children);
6356                }
6357                // A non-loop block entry deletes at most one token (single-token
6358                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6359                // is reported at the first token instead of skipping ahead.
6360                if !loop_sync {
6361                    break;
6362                }
6363                cursor = next;
6364            }
6365        }
6366        if nullable {
6367            self.generated_sync_expected = Some(expected);
6368            return Ok(Vec::new());
6369        }
6370        let current = self.input.lt(1);
6371        let expected_symbols = expected.to_btree_set();
6372        Err(AntlrError::ParserError {
6373            line: current.as_ref().map(Token::line).unwrap_or_default(),
6374            column: current.as_ref().map(Token::column).unwrap_or_default(),
6375            message: format!(
6376                "mismatched input {} expecting {}",
6377                current
6378                    .as_ref()
6379                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6380                self.expected_symbols_display(&expected_symbols)
6381            ),
6382        })
6383    }
6384
6385    /// Returns a generated-parser prediction when one token of lookahead
6386    /// uniquely selects an alternative for `state_number`.
6387    ///
6388    /// This mirrors the interpreter's LL(1) commit point and lets generated
6389    /// recursive-descent methods avoid invoking the adaptive simulator for
6390    /// simple optional/block/loop decisions.
6391    pub fn ll1_decision_prediction(
6392        &mut self,
6393        atn: &Atn,
6394        state_number: usize,
6395    ) -> Option<ParserAtnPrediction> {
6396        let state = atn.state(state_number)?;
6397        if state.precedence_rule_decision() {
6398            return None;
6399        }
6400        let rule_stop = state
6401            .rule_index()
6402            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6403        let symbol = self.la(1);
6404        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6405        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6406            alt: alt + 1,
6407            requires_full_context: false,
6408            has_semantic_context: false,
6409            diagnostic: None,
6410        })
6411    }
6412
6413    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6414        let mut expected = BTreeSet::new();
6415        for index in (1..self.rule_context_stack.len()).rev() {
6416            let invoking_state = self.rule_context_stack[index].invoking_state;
6417            let Ok(state_number) = usize::try_from(invoking_state) else {
6418                continue;
6419            };
6420            let Some(Transition::Rule { follow_state, .. }) = atn
6421                .state(state_number)
6422                .and_then(|state| state.transitions().first())
6423                .map(ParserTransition::data)
6424            else {
6425                continue;
6426            };
6427            let return_state = follow_state;
6428            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6429            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
6430                return expected;
6431            }
6432        }
6433        expected.insert(TOKEN_EOF);
6434        expected
6435    }
6436
6437    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6438        let mut expected = TokenBitSet::default();
6439        for index in (1..self.rule_context_stack.len()).rev() {
6440            let invoking_state = self.rule_context_stack[index].invoking_state;
6441            let Ok(state_number) = usize::try_from(invoking_state) else {
6442                continue;
6443            };
6444            let Some(Transition::Rule { follow_state, .. }) = atn
6445                .state(state_number)
6446                .and_then(|state| state.transitions().first())
6447                .map(ParserTransition::data)
6448            else {
6449                continue;
6450            };
6451            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
6452            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6453                return expected;
6454            }
6455        }
6456        expected.insert(TOKEN_EOF);
6457        expected
6458    }
6459
6460    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
6461    /// without materializing the union.
6462    ///
6463    /// This walks the rule-invocation stack directly, innermost frame first —
6464    /// the same frames, in the same order, with the same rule-stop gating as
6465    /// the same outer-context return-state chain used by adaptive prediction.
6466    /// The nullable
6467    /// exit in `sync_decision` asks only this membership question, and on
6468    /// valid input the innermost frame answers it, so the early exit replaces
6469    /// an O(stack-depth) set union per loop/optional exit with one probe.
6470    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
6471        for index in (1..self.rule_context_stack.len()).rev() {
6472            let invoking_state = self.rule_context_stack[index].invoking_state;
6473            let Ok(state_number) = usize::try_from(invoking_state) else {
6474                continue;
6475            };
6476            let Some(Transition::Rule { follow_state, .. }) = atn
6477                .state(state_number)
6478                .and_then(|state| state.transitions().first())
6479                .map(ParserTransition::data)
6480            else {
6481                continue;
6482            };
6483            if self
6484                .cached_state_expected_token_set(atn, follow_state)
6485                .contains(symbol)
6486            {
6487                return true;
6488            }
6489            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6490                return false;
6491            }
6492        }
6493        symbol == TOKEN_EOF
6494    }
6495
6496    /// Builds a generated no-viable-alternative parser error.
6497    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
6498        let error_index = self.input.index();
6499        self.no_viable_alternative_error_at(start_index, error_index)
6500    }
6501
6502    /// Builds a generated no-viable-alternative parser error at the simulator's
6503    /// failing lookahead index. `adaptive_predict` restores the input cursor
6504    /// before returning, so generated parsers have to pass the recorded index
6505    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
6506    pub fn no_viable_alternative_error_at(
6507        &self,
6508        start_index: usize,
6509        error_index: usize,
6510    ) -> AntlrError {
6511        let diagnostic = self.no_viable_alternative(start_index, error_index);
6512        AntlrError::ParserError {
6513            line: diagnostic.line,
6514            column: diagnostic.column,
6515            message: diagnostic.message,
6516        }
6517    }
6518
6519    /// Builds a generated failed-predicate parser error.
6520    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
6521        let current = self.input.lt(1);
6522        AntlrError::ParserError {
6523            line: current.as_ref().map(Token::line).unwrap_or_default(),
6524            column: current.as_ref().map(Token::column).unwrap_or_default(),
6525            message: format!("rule failed predicate: {}", message.into()),
6526        }
6527    }
6528
6529    /// Builds a generated parser error for a semantic predicate with ANTLR's
6530    /// `<fail='...'>` option.
6531    pub fn failed_predicate_option_error(
6532        &self,
6533        rule_index: usize,
6534        message: impl Into<String>,
6535    ) -> AntlrError {
6536        let current = self.input.lt(1);
6537        let rule_name = self
6538            .rule_names()
6539            .get(rule_index)
6540            .map_or_else(|| rule_index.to_string(), Clone::clone);
6541        AntlrError::ParserError {
6542            line: current.as_ref().map(Token::line).unwrap_or_default(),
6543            column: current.as_ref().map(Token::column).unwrap_or_default(),
6544            message: format!("rule {rule_name} {}", message.into()),
6545        }
6546    }
6547
6548    /// Builds a generated parser-action event at the current input position.
6549    pub fn parser_action_at_current(
6550        &mut self,
6551        source_state: usize,
6552        rule_index: usize,
6553        start_index: usize,
6554        consumed_eof: bool,
6555    ) -> ParserAction {
6556        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6557        ParserAction::new(source_state, rule_index, start_index, stop_index)
6558    }
6559
6560    /// Offers a committed parser action event to the user semantic hook.
6561    ///
6562    /// Generated parsers call this for action source states that were present
6563    /// in the ATN but not translated into a built-in Rust action template.
6564    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
6565        let rule_index = action.rule_index();
6566        let rule_name = self.rule_names().get(rule_index).cloned();
6567        let context = None;
6568        let input = &mut self.input;
6569        let semantic_hooks = &mut self.semantic_hooks;
6570        let member_values = &self.int_members;
6571        let mut ctx = ParserSemCtx {
6572            input,
6573            tree_storage: &self.tree,
6574            rule_index,
6575            coordinate_index: usize::MAX,
6576            rule_name,
6577            context,
6578            tree: Some(tree),
6579            local_int_arg: None,
6580            member_values,
6581            action: Some(action),
6582        };
6583        let handled = semantic_hooks.action(&mut ctx, action);
6584        // This action reached the hook because it had no translated arm. If no
6585        // hook handled it either (`SemanticHooks::action` returns `false`), the
6586        // committed action is silently dropped — record it so the parse entry
6587        // can fail loud under the fail-loud boundary, mirroring unknown
6588        // predicates. `assume-*` policies opt out of the fail-loud recording.
6589        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
6590            let coordinate = (rule_index, action.source_state());
6591            if !self.unhandled_action_hits.contains(&coordinate) {
6592                self.unhandled_action_hits.push(coordinate);
6593            }
6594        }
6595        handled
6596    }
6597
6598    /// Attempts to execute a whole generated rule by committing simulator
6599    /// decisions directly. Unsupported constructs or decisions that need
6600    /// full-context / predicate evaluation restore the input cursor and fall
6601    /// back to [`Self::parse_atn_rule`].
6602    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
6603        &mut self,
6604        atn: &'atn Atn,
6605        simulator: &mut ParserAtnSimulator<'atn>,
6606        rule_index: usize,
6607    ) -> Result<ParseTree, AntlrError> {
6608        let start_index = self.current_visible_index();
6609        self.clear_prediction_diagnostics();
6610        self.reset_per_parse_caches();
6611        self.reset_recognition_arena();
6612        let tree_checkpoint = self.tree.checkpoint();
6613        let mut decision_by_state = vec![None; atn.states().len()];
6614        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
6615            if let Some(slot) = decision_by_state.get_mut(state_number) {
6616                *slot = Some(decision);
6617            }
6618        }
6619
6620        let result = DirectAdaptiveParser {
6621            parser: self,
6622            atn,
6623            simulator,
6624            decision_by_state,
6625            steps: 0,
6626        }
6627        .parse_rule(rule_index, -1, 0);
6628
6629        match result {
6630            Ok(tree) => {
6631                self.report_token_source_errors();
6632                self.release_tree_scratch_if_idle();
6633                Ok(tree)
6634            }
6635            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
6636                let _ = reason;
6637                self.tree.rollback(tree_checkpoint);
6638                self.input.seek(start_index);
6639                self.parse_atn_rule(atn, rule_index)
6640            }
6641        }
6642    }
6643
6644    /// Parses a generated rule by interpreting the parser ATN from the rule's
6645    /// start state to its stop state.
6646    ///
6647    /// The recognizer backtracks across alternatives and loop exits using token
6648    /// stream indices instead of committing to input consumption immediately.
6649    /// Once a viable ATN path is found, the parser commits the accepted token
6650    /// interval and returns a rule node whose children mirror every grammar
6651    /// rule invocation reached on that path, matching ANTLR's parse-tree
6652    /// shape.
6653    pub fn parse_atn_rule(
6654        &mut self,
6655        atn: &Atn,
6656        rule_index: usize,
6657    ) -> Result<ParseTree, AntlrError> {
6658        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
6659    }
6660
6661    /// Parses a generated rule by interpreting the parser ATN with an initial
6662    /// left-recursive precedence threshold.
6663    pub fn parse_atn_rule_with_precedence(
6664        &mut self,
6665        atn: &Atn,
6666        rule_index: usize,
6667        precedence: i32,
6668    ) -> Result<ParseTree, AntlrError> {
6669        self.parse_atn_rule_with_precedence_inner(
6670            atn,
6671            rule_index,
6672            precedence,
6673            None,
6674            AltNumberTracking::default(),
6675        )
6676    }
6677
6678    fn parse_atn_rule_with_precedence_inner(
6679        &mut self,
6680        atn: &Atn,
6681        rule_index: usize,
6682        precedence: i32,
6683        predicate_context: Option<FastPredicateContext<'_>>,
6684        alt_tracking: AltNumberTracking,
6685    ) -> Result<ParseTree, AntlrError> {
6686        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6687            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6688        })?;
6689        let stop_state = atn
6690            .rule_to_stop_state()
6691            .get(rule_index)
6692            .filter(|state| *state != usize::MAX)
6693            .ok_or_else(|| {
6694                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6695            })?;
6696
6697        let start_index = self.current_visible_index();
6698        self.clear_prediction_diagnostics();
6699        self.reset_per_parse_caches();
6700        self.reset_recognition_arena();
6701        let caller_follow_state = self.pending_invoking_follow_state(atn);
6702        self.fast_recovery_enabled = false;
6703        self.fast_token_nodes_enabled = false;
6704        self.fast_track_alt_numbers = alt_tracking.any();
6705        let top_request = FastRecognizeTopRequest {
6706            start_state,
6707            stop_state,
6708            start_index,
6709            precedence,
6710            caller_follow_state,
6711        };
6712        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
6713        self.fast_token_nodes_enabled = self.build_parse_trees;
6714        let needs_tree_retry = matches!(
6715            &first_pass,
6716            Ok((outcome, _, _))
6717                if self.build_parse_trees
6718                    && self
6719                        .recognition_arena
6720                        .sequence_has_left_recursive_boundary(outcome.nodes)
6721        );
6722        let needs_retry = match &first_pass {
6723            // The FIRST-set prefilter trims speculative rule calls that can't
6724            // match the current lookahead — useful for perf on grammars with
6725            // many epsilon-reachable rules, but the trim also bypasses
6726            // single-token insertion / deletion recovery that ANTLR's
6727            // reference parser runs at the child rule's first consuming
6728            // transition. Retry without the prefilter whenever the first pass
6729            // either produced no outcome at all or produced a recovered
6730            // outcome (diagnostics non-empty), since the second pass might
6731            // surface a child-level recovery with cleaner diagnostics or
6732            // closer parity to ANTLR's tree shape. Left-recursive tree
6733            // boundaries also need the token-node pass; otherwise the fold has
6734            // no concrete left operand to wrap into ANTLR's recursive context.
6735            Err(_) => true,
6736            Ok((outcome, _, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
6737        };
6738        let (outcome, _expected, alt_number) = if needs_retry {
6739            self.fast_first_set_prefilter = false;
6740            self.fast_recovery_enabled = false;
6741            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6742            let clean_selected = if needs_tree_retry {
6743                match clean_retry {
6744                    ok @ Ok(_) => ok,
6745                    Err(_) => first_pass,
6746                }
6747            } else {
6748                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
6749            };
6750            let selected = if clean_selected.is_err()
6751                || matches!(&clean_selected, Ok((outcome, _, _)) if !outcome.diagnostics.is_empty())
6752            {
6753                self.fast_recovery_enabled = true;
6754                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6755                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
6756            } else {
6757                clean_selected
6758            };
6759            self.fast_first_set_prefilter = true;
6760            self.fast_recovery_enabled = true;
6761            selected.map_err(|expected| {
6762                if predicate_context.is_some()
6763                    && let Some(error) = self.unknown_semantic_error()
6764                {
6765                    self.report_token_source_errors();
6766                    return error;
6767                }
6768                let error = self.recognition_error(rule_index, start_index, &expected);
6769                self.record_syntax_errors(1);
6770                self.report_token_source_errors();
6771                error
6772            })?
6773        } else {
6774            first_pass.expect("first_pass is Ok in the no-retry branch")
6775        };
6776        if predicate_context.is_some()
6777            && let Some(error) = self.unknown_semantic_error()
6778        {
6779            self.report_token_source_errors();
6780            return Err(error);
6781        }
6782        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6783        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
6784        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6785        self.report_token_source_errors();
6786        let mut context = ParserRuleContext::with_child_capacity(
6787            rule_index,
6788            self.state(),
6789            if self.build_parse_trees {
6790                self.recognition_arena.sequence_len(outcome.nodes)
6791            } else {
6792                0
6793            },
6794        );
6795        if alt_tracking.public {
6796            context.set_alt_number(alt_number);
6797        }
6798        if alt_tracking.context {
6799            context.set_context_alt_number(alt_number);
6800        }
6801        if let Some(token) = self.token_id_at(start_index) {
6802            self.set_context_start(&mut context, token);
6803        }
6804        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
6805        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
6806            self.set_context_stop(&mut context, token);
6807        }
6808        let live_root = if self.build_parse_trees {
6809            self.recognition_arena
6810                .fold_left_recursive_boundaries(outcome.nodes)
6811        } else {
6812            outcome.nodes
6813        };
6814        if self.build_parse_trees {
6815            if self
6816                .recognition_arena
6817                .sequence_has_explicit_token(live_root)
6818            {
6819                let mut cursor = live_root;
6820                while let Some(link) = self.recognition_arena.link(cursor) {
6821                    let child = self.arena_recognized_node_tree(
6822                        link.head,
6823                        alt_tracking.public,
6824                        alt_tracking.context,
6825                    )?;
6826                    self.tree.add_child(&mut context, child);
6827                    cursor = link.tail;
6828                }
6829            } else {
6830                self.add_arena_implicit_token_children(
6831                    &mut context,
6832                    start_index,
6833                    stop_index,
6834                    live_root,
6835                    alt_tracking,
6836                )?;
6837            }
6838        }
6839        self.finish_recognition_arena(live_root, outcome.diagnostics);
6840        self.input.seek(outcome.index);
6841
6842        let tree = self.rule_node(context);
6843        self.release_tree_scratch_if_idle();
6844        Ok(tree)
6845    }
6846
6847    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
6848        let invoking_state = self.pending_invoking_states.last().copied()?;
6849        let state_number = usize::try_from(invoking_state).ok()?;
6850        match atn.state(state_number)?.transitions().first()?.data() {
6851            Transition::Rule { follow_state, .. } => Some(follow_state),
6852            _ => None,
6853        }
6854    }
6855
6856    #[cfg(test)]
6857    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
6858        caller_follow_token_info_for_stream(&mut self.input, index)
6859    }
6860
6861    /// Runs the fast recognizer once from the rule's start state and returns
6862    /// the best outcome or the per-attempt expected-token accumulator. The
6863    /// caller flips `fast_first_set_prefilter` between calls when a retry is
6864    /// needed, so the FIRST-set cache is left intact across both passes.
6865    fn fast_recognize_top(
6866        &mut self,
6867        atn: &Atn,
6868        request: FastRecognizeTopRequest,
6869        predicate_context: Option<FastPredicateContext<'_>>,
6870    ) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
6871        let FastRecognizeTopRequest {
6872            start_state,
6873            stop_state,
6874            start_index,
6875            precedence,
6876            caller_follow_state,
6877        } = request;
6878        // `input.size()` is intentionally only the currently buffered token
6879        // count here. Do not restore an up-front fill just to size this map:
6880        // a small floor avoids tiny-input churn, and larger inputs reserve from
6881        // the buffered token count without forcing startup tokenization. The
6882        // 8x multiplier matches the empirical
6883        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
6884        // Kotlin ~12× memo entries per token), so the table avoids one
6885        // rehash on the typical hot path.
6886        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
6887        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
6888        recognize_scratch.prepare(memo_capacity);
6889        let mut expected = ExpectedTokens::default();
6890        let empty_recovery = self.empty_recovery_symbols();
6891        let outcomes = self.recognize_state_fast(
6892            atn,
6893            FastRecognizeRequest {
6894                state_number: start_state,
6895                stop_state,
6896                index: start_index,
6897                rule_start_index: start_index,
6898                decision_start_index: None,
6899                precedence,
6900                depth: 0,
6901                recovery_symbols: empty_recovery,
6902                recovery_state: None,
6903            },
6904            FastRecognizeScratch {
6905                predicate_context,
6906                visiting: &mut recognize_scratch.visiting,
6907                memo: &mut recognize_scratch.memo,
6908                expected: &mut expected,
6909                native_depth: 0,
6910            },
6911        );
6912        recognize_scratch.release_oversized_memo();
6913        self.fast_recognize_scratch = recognize_scratch;
6914        #[cfg(feature = "perf-counters")]
6915        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
6916            perf_counters::dump();
6917            perf_counters::reset();
6918        }
6919        let caller_follow =
6920            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
6921        let selected = {
6922            let arena = &self.recognition_arena;
6923            let input = &mut self.input;
6924            select_best_fast_outcome(
6925                outcomes.into_iter(),
6926                self.prediction_mode,
6927                caller_follow.as_deref(),
6928                |index| caller_follow_token_info_for_stream(input, index),
6929                arena,
6930            )
6931        };
6932        match selected {
6933            Some(mut outcome) => {
6934                let alt_number = if self.build_parse_trees || self.fast_track_alt_numbers {
6935                    self.materialize_fast_outcome_nodes(&mut outcome)
6936                } else {
6937                    0
6938                };
6939                Ok((outcome, expected, alt_number))
6940            }
6941            None => Err(expected),
6942        }
6943    }
6944
6945    /// Converts one speculative arena record into the flat public CST.
6946    fn arena_recognized_node_tree(
6947        &mut self,
6948        node_id: RecognizedNodeId,
6949        track_alt_numbers: bool,
6950        track_context_alt_numbers: bool,
6951    ) -> Result<ParseTree, AntlrError> {
6952        let node = self.recognition_arena.node(node_id);
6953        match node {
6954            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
6955            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
6956            ArenaRecognizedNode::MissingToken { extra } => {
6957                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
6958                    RecognitionExtra::MissingToken {
6959                        token_type,
6960                        at_index,
6961                        text,
6962                    } => (*token_type, *at_index as usize, text.clone()),
6963                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
6964                        unreachable!("missing-token node must reference missing-token extra")
6965                    }
6966                };
6967                let (line, column) = self
6968                    .token_at(at_index)
6969                    .map_or((0, 0), |token| (token.line(), token.column()));
6970                let token = self.insert_synthetic_token(token_type, text, line, column)?;
6971                Ok(self.error_tree(token))
6972            }
6973            ArenaRecognizedNode::Rule {
6974                rule_index,
6975                invoking_state,
6976                alt_number,
6977                start_index,
6978                stop_index,
6979                return_values,
6980                children,
6981            } => {
6982                let mut context = ParserRuleContext::with_child_capacity(
6983                    rule_index as usize,
6984                    invoking_state as isize,
6985                    self.recognition_arena.sequence_len(children),
6986                );
6987                if track_alt_numbers {
6988                    context.set_alt_number(alt_number as usize);
6989                }
6990                if track_context_alt_numbers {
6991                    context.set_context_alt_number(alt_number as usize);
6992                }
6993                if let Some(extra) = return_values {
6994                    let RecognitionExtra::ReturnValues(values) =
6995                        self.recognition_arena.extra(extra)
6996                    else {
6997                        unreachable!("rule node must reference return-values extra");
6998                    };
6999                    for (name, value) in values {
7000                        context.set_int_return(name.clone(), *value);
7001                    }
7002                }
7003                if let Some(token) = self.token_id_at(start_index as usize) {
7004                    self.set_context_start(&mut context, token);
7005                }
7006                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7007                    self.set_context_stop(&mut context, token);
7008                }
7009                let mut cursor = self
7010                    .recognition_arena
7011                    .fold_left_recursive_boundaries(children);
7012                while let Some(link) = self.recognition_arena.link(cursor) {
7013                    let child = self.arena_recognized_node_tree(
7014                        link.head,
7015                        track_alt_numbers,
7016                        track_context_alt_numbers,
7017                    )?;
7018                    self.tree.add_child(&mut context, child);
7019                    cursor = link.tail;
7020                }
7021                Ok(self.rule_node(context))
7022            }
7023            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index, .. } => {
7024                Err(AntlrError::Unsupported(format!(
7025                    "unfolded left-recursive boundary for rule {rule_index}"
7026                )))
7027            }
7028        }
7029    }
7030
7031    fn arena_recognized_node_tree_with_implicit_tokens(
7032        &mut self,
7033        node_id: RecognizedNodeId,
7034        alt_tracking: AltNumberTracking,
7035    ) -> Result<ParseTree, AntlrError> {
7036        let node = self.recognition_arena.node(node_id);
7037        match node {
7038            ArenaRecognizedNode::Rule {
7039                rule_index,
7040                invoking_state,
7041                alt_number,
7042                start_index,
7043                stop_index,
7044                children,
7045                ..
7046            } => {
7047                let mut context = ParserRuleContext::with_child_capacity(
7048                    rule_index as usize,
7049                    invoking_state as isize,
7050                    self.recognition_arena.sequence_len(children),
7051                );
7052                if alt_tracking.public {
7053                    context.set_alt_number(alt_number as usize);
7054                }
7055                if alt_tracking.context {
7056                    context.set_context_alt_number(alt_number as usize);
7057                }
7058                if let Some(token) = self.token_id_at(start_index as usize) {
7059                    self.set_context_start(&mut context, token);
7060                }
7061                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7062                    self.set_context_stop(&mut context, token);
7063                }
7064                let children = self
7065                    .recognition_arena
7066                    .fold_left_recursive_boundaries(children);
7067                self.add_arena_implicit_token_children(
7068                    &mut context,
7069                    start_index as usize,
7070                    stop_index.map(|index| index as usize),
7071                    children,
7072                    alt_tracking,
7073                )?;
7074                Ok(self.rule_node(context))
7075            }
7076            _ => {
7077                self.arena_recognized_node_tree(node_id, alt_tracking.public, alt_tracking.context)
7078            }
7079        }
7080    }
7081
7082    fn add_arena_implicit_token_children(
7083        &mut self,
7084        context: &mut ParserRuleContext,
7085        start_index: usize,
7086        stop_index: Option<usize>,
7087        mut children: NodeSeqId,
7088        alt_tracking: AltNumberTracking,
7089    ) -> Result<(), AntlrError> {
7090        let mut cursor = Some(start_index);
7091        while let Some(link) = self.recognition_arena.link(children) {
7092            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
7093                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
7094                let child =
7095                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7096                self.tree.add_child(context, child);
7097                if let Some(child_stop) = child_stop {
7098                    cursor = self.next_visible_after_token(child_stop);
7099                }
7100            } else {
7101                let child =
7102                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7103                self.tree.add_child(context, child);
7104            }
7105            children = link.tail;
7106        }
7107        if let Some(stop) = stop_index {
7108            self.add_visible_terminals_through(context, cursor, stop)?;
7109        }
7110        Ok(())
7111    }
7112
7113    fn add_visible_terminals_before(
7114        &mut self,
7115        context: &mut ParserRuleContext,
7116        cursor: &mut Option<usize>,
7117        before: usize,
7118    ) -> Result<(), AntlrError> {
7119        let Some(stop) = before.checked_sub(1) else {
7120            return Ok(());
7121        };
7122        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
7123        *cursor = next;
7124        Ok(())
7125    }
7126
7127    fn add_visible_terminals_through(
7128        &mut self,
7129        context: &mut ParserRuleContext,
7130        mut cursor: Option<usize>,
7131        stop: usize,
7132    ) -> Result<Option<usize>, AntlrError> {
7133        while let Some(index) = cursor {
7134            if index > stop {
7135                return Ok(Some(index));
7136            }
7137            let token = self
7138                .input
7139                .get_id(index)
7140                .ok_or_else(|| AntlrError::ParserError {
7141                    line: 0,
7142                    column: 0,
7143                    message: format!("missing token at index {index}"),
7144                })?;
7145            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
7146            let child = self.terminal_tree(token);
7147            self.tree.add_child(context, child);
7148            if is_eof {
7149                return Ok(None);
7150            }
7151            cursor = self.next_visible_after_token(index);
7152        }
7153        Ok(None)
7154    }
7155
7156    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
7157        let next = self.input.next_visible_after(index);
7158        (next != index).then_some(next)
7159    }
7160
7161    /// Parses a generated rule and returns semantic actions reached on the
7162    /// selected ATN path.
7163    ///
7164    /// This slower path preserves action ordering and token intervals for
7165    /// generated code that replays target-specific action templates after the
7166    /// recognizer has chosen one viable parse path.
7167    pub fn parse_atn_rule_with_actions(
7168        &mut self,
7169        atn: &Atn,
7170        rule_index: usize,
7171    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7172        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
7173    }
7174
7175    /// Parses a generated rule and emits ATN actions plus selected rule-init
7176    /// actions reached on the chosen path.
7177    ///
7178    /// Generated parsers use this when a grammar contains rule-level `@init`
7179    /// templates that must run for nested rule invocations. The runtime keeps
7180    /// the action list path-sensitive, so init templates are replayed only for
7181    /// rules that were actually entered by the selected parse.
7182    pub fn parse_atn_rule_with_action_inits(
7183        &mut self,
7184        atn: &Atn,
7185        rule_index: usize,
7186        init_action_rules: &[usize],
7187    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7188        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7189    }
7190
7191    /// Parses a generated rule with optional semantic-action replay features.
7192    ///
7193    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7194    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7195    /// unchanged for grammars that do not request that target template.
7196    pub fn parse_atn_rule_with_action_options(
7197        &mut self,
7198        atn: &Atn,
7199        rule_index: usize,
7200        init_action_rules: &[usize],
7201        track_alt_numbers: bool,
7202    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7203        self.parse_atn_rule_with_runtime_options(
7204            atn,
7205            rule_index,
7206            ParserRuntimeOptions {
7207                init_action_rules,
7208                track_alt_numbers,
7209                ..ParserRuntimeOptions::default()
7210            },
7211        )
7212    }
7213
7214    /// Parses a generated rule with action replay and parser predicate support.
7215    ///
7216    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7217    /// target-template predicate semantics emitted by the generator. Missing
7218    /// entries are treated as true so unsupported predicate-free grammars keep
7219    /// the previous unconditional transition behavior.
7220    pub fn parse_atn_rule_with_runtime_options(
7221        &mut self,
7222        atn: &Atn,
7223        rule_index: usize,
7224        options: ParserRuntimeOptions<'_>,
7225    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7226        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7227    }
7228
7229    /// Parses a generated rule with action replay, parser predicate support,
7230    /// and an initial left-recursive precedence threshold.
7231    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7232        &mut self,
7233        atn: &Atn,
7234        rule_index: usize,
7235        precedence: i32,
7236        options: ParserRuntimeOptions<'_>,
7237    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7238        let ParserRuntimeOptions {
7239            init_action_rules,
7240            track_alt_numbers,
7241            track_context_alt_numbers,
7242            predicates,
7243            semantics,
7244            rule_args,
7245            member_actions,
7246            return_actions,
7247            unknown_predicate_policy,
7248        } = options;
7249        let capture_alt_numbers = track_alt_numbers || track_context_alt_numbers;
7250        if init_action_rules.is_empty()
7251            && !capture_alt_numbers
7252            && predicates.is_empty()
7253            && semantics.is_none()
7254            && rule_args.is_empty()
7255            && member_actions.is_empty()
7256            && return_actions.is_empty()
7257            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7258            && !atn_has_observable_action_transitions(atn)
7259            && !self.semantic_hooks.observes_parser_decisions()
7260            && (!self.semantic_hooks.observes_parser_predicates()
7261                || !atn_has_predicate_transitions(atn))
7262        {
7263            return self
7264                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7265                .map(|tree| (tree, Vec::new()));
7266        }
7267        if !self.semantic_hooks.observes_parser_decisions()
7268            && can_use_fast_predicate_recognizer(atn, &options)
7269        {
7270            self.unknown_predicate_policy = unknown_predicate_policy;
7271            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7272            let member_values = self.int_members.clone();
7273            let result = self
7274                .parse_atn_rule_with_precedence_inner(
7275                    atn,
7276                    rule_index,
7277                    precedence,
7278                    Some(FastPredicateContext {
7279                        predicates,
7280                        semantics,
7281                        member_values: &member_values,
7282                    }),
7283                    AltNumberTracking {
7284                        public: track_alt_numbers,
7285                        context: track_context_alt_numbers,
7286                    },
7287                )
7288                .map(|tree| (tree, Vec::new()));
7289            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7290                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7291            }
7292            return result;
7293        }
7294        self.unknown_predicate_policy = unknown_predicate_policy;
7295        // A generated parent may have already recorded unknown-predicate
7296        // coordinates before descending into this (interpreted) child. Clearing
7297        // unconditionally would drop them before the parent's public entry
7298        // surfaces them, so stash and restore around this call: recognition sees
7299        // only the hits it records itself (so the fail-loud check below reflects
7300        // this rule), and the parent's prior hits are merged back afterward.
7301        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7302        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7303            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7304        })?;
7305        let stop_state = atn
7306            .rule_to_stop_state()
7307            .get(rule_index)
7308            .filter(|state| *state != usize::MAX)
7309            .ok_or_else(|| {
7310                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7311            })?;
7312
7313        let start_index = self.current_visible_index();
7314        self.clear_prediction_diagnostics();
7315        self.reset_per_parse_caches();
7316        self.reset_recognition_arena();
7317        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7318        let invoking_state = self.pending_invoking_states.pop();
7319        let local_int_arg = invoking_state
7320            .and_then(|state| usize::try_from(state).ok())
7321            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7322        let mut visiting = BTreeSet::new();
7323        let mut memo = BTreeMap::new();
7324        let mut expected = ExpectedTokens::default();
7325        let member_values = self.int_members.clone();
7326        let return_values = BTreeMap::new();
7327        let outcomes = self.recognize_state(
7328            atn,
7329            RecognizeRequest {
7330                state_number: start_state,
7331                stop_state,
7332                index: start_index,
7333                rule_start_index: start_index,
7334                decision_start_index: None,
7335                init_action_rules: &init_action_rules,
7336                predicates,
7337                semantics,
7338                rule_args,
7339                member_actions,
7340                return_actions,
7341                local_int_arg,
7342                member_values,
7343                return_values,
7344                rule_alt_number: 0,
7345                track_alt_numbers: capture_alt_numbers,
7346                consumed_eof: false,
7347                committed_decision: false,
7348                precedence,
7349                depth: 0,
7350                recovery_symbols: BTreeSet::new(),
7351                recovery_state: None,
7352            },
7353            &mut visiting,
7354            &mut memo,
7355            &mut expected,
7356        );
7357        if let Some(error) = self.unknown_semantic_error() {
7358            self.report_token_source_errors();
7359            // Keep the recorded coordinates: when this interpreted rule is a
7360            // child of a generated parent, the parent's catch block recovers an
7361            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7362            // coordinate must survive on the parser for the top-level entry's
7363            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7364            // is handled by clearing at the top-level generated entry instead.
7365            return Err(error);
7366        }
7367        // Recognition recorded no unresolved coordinate of its own; merge the
7368        // parent's prior hits back so its public entry can still surface them.
7369        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7370        let Some(outcome) = select_best_outcome(
7371            outcomes.into_iter(),
7372            self.prediction_mode,
7373            &self.recognition_arena,
7374        ) else {
7375            let error = self.recognition_error(rule_index, start_index, &expected);
7376            self.record_syntax_errors(1);
7377            self.report_token_source_errors();
7378            return Err(error);
7379        };
7380
7381        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7382        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7383        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7384        self.report_token_source_errors();
7385        let mut actions = outcome.actions;
7386        if init_action_rules.contains(&rule_index) {
7387            actions.insert(
7388                0,
7389                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7390            );
7391        }
7392        let mut context =
7393            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7394        if track_alt_numbers {
7395            context.set_alt_number(outcome.alt_number);
7396        }
7397        if track_context_alt_numbers {
7398            context.set_context_alt_number(outcome.alt_number);
7399        }
7400        for (name, value) in outcome.return_values {
7401            context.set_int_return(name, value);
7402        }
7403        if let Some(token) = self.token_id_at(start_index) {
7404            self.set_context_start(&mut context, token);
7405        }
7406        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7407            self.set_context_stop(&mut context, token);
7408        }
7409        let live_root = if self.build_parse_trees {
7410            self.recognition_arena
7411                .fold_left_recursive_boundaries(outcome.nodes)
7412        } else {
7413            outcome.nodes
7414        };
7415        if self.build_parse_trees {
7416            let mut nodes = live_root;
7417            while let Some(link) = self.recognition_arena.link(nodes) {
7418                let child = self.arena_recognized_node_tree(
7419                    link.head,
7420                    track_alt_numbers,
7421                    track_context_alt_numbers,
7422                )?;
7423                self.tree.add_child(&mut context, child);
7424                nodes = link.tail;
7425            }
7426        }
7427        self.finish_recognition_arena(live_root, outcome.diagnostics);
7428        self.input.seek(outcome.index);
7429
7430        let tree = self.rule_node(context);
7431        self.release_tree_scratch_if_idle();
7432        Ok((tree, actions))
7433    }
7434
7435    /// Temporary parser entry used by generated parser methods while the parser
7436    /// ATN simulator is being implemented.
7437    ///
7438    /// This keeps generated parser crates buildable and gives us a stable method
7439    /// surface for every grammar rule. It intentionally accepts all remaining
7440    /// tokens into one rule context; it is not the final parser semantics.
7441    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
7442        let mut context = ParserRuleContext::new(rule_index, self.state());
7443        while self.la(1) != TOKEN_EOF {
7444            let token_type = self.la(1);
7445            let child = self.match_token(token_type)?;
7446            if self.build_parse_trees {
7447                self.tree.add_child(&mut context, child);
7448            }
7449        }
7450        if self.build_parse_trees {
7451            let child = self.match_eof()?;
7452            self.tree.add_child(&mut context, child);
7453        }
7454        let tree = self.rule_node(context);
7455        self.release_tree_scratch_if_idle();
7456        Ok(tree)
7457    }
7458
7459    /// Builds the parser error reported when no ATN path can reach the active
7460    /// rule stop state.
7461    fn recognition_error(
7462        &mut self,
7463        rule_index: usize,
7464        start_index: usize,
7465        expected: &ExpectedTokens,
7466    ) -> AntlrError {
7467        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
7468        self.input.seek(index);
7469        let current = self.input.lt(1);
7470        let line = current.as_ref().map(Token::line).unwrap_or_default();
7471        let column = current.as_ref().map(Token::column).unwrap_or_default();
7472        AntlrError::ParserError {
7473            line,
7474            column,
7475            message,
7476        }
7477    }
7478
7479    /// Builds the token index and ANTLR-compatible message for a failed rule.
7480    fn expected_error_message(
7481        &mut self,
7482        rule_index: usize,
7483        start_index: usize,
7484        expected: &ExpectedTokens,
7485    ) -> (usize, String) {
7486        let index = expected
7487            .index
7488            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
7489            .unwrap_or_else(|| self.input.index());
7490        self.input.seek(index);
7491        let current = self.input.lt(1);
7492        let message = if expected
7493            .no_viable
7494            .as_ref()
7495            .is_some_and(|no_viable| no_viable.error_index == index)
7496        {
7497            let start = expected
7498                .no_viable
7499                .as_ref()
7500                .map_or(start_index, |no_viable| no_viable.start_index);
7501            let text = display_input_text(&self.input.text(start, index));
7502            format!("no viable alternative at input '{text}'")
7503        } else if expected.symbols.is_empty() {
7504            if expected.index.is_some() {
7505                let found = current
7506                    .as_ref()
7507                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7508                if current
7509                    .as_ref()
7510                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7511                {
7512                    format!(
7513                        "missing {} at {found}",
7514                        self.expected_symbols_display(&expected.symbols)
7515                    )
7516                } else {
7517                    format!("mismatched input {found}")
7518                }
7519            } else {
7520                format!("no viable alternative while parsing rule {rule_index}")
7521            }
7522        } else {
7523            format!(
7524                "mismatched input {} expecting {}",
7525                current
7526                    .as_ref()
7527                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7528                self.expected_symbols_display(&expected.symbols)
7529            )
7530        };
7531        (index, message)
7532    }
7533
7534    /// Converts a failed child rule into a recovered outcome so the parent can
7535    /// continue after reporting the child diagnostic.
7536    fn child_rule_failure_recovery(
7537        &mut self,
7538        rule_index: usize,
7539        start_index: usize,
7540        sync_symbols: &BTreeSet<i32>,
7541        member_values: BTreeMap<usize, i64>,
7542        expected: &ExpectedTokens,
7543    ) -> Option<RecognizeOutcome> {
7544        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7545        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7546        let mut next_index = error_index;
7547        loop {
7548            let symbol = self.token_type_at(next_index);
7549            if sync_symbols.contains(&symbol) {
7550                if next_index == error_index {
7551                    return None;
7552                }
7553                break;
7554            }
7555            if symbol == TOKEN_EOF {
7556                break;
7557            }
7558            let after = self.consume_index(next_index, symbol);
7559            if after == next_index {
7560                break;
7561            }
7562            next_index = after;
7563        }
7564        let mut nodes = NodeSeqId::EMPTY;
7565        let error = self.arena_token_node(error_index, true);
7566        self.arena_prepend(&mut nodes, error);
7567        let diagnostics = self
7568            .recognition_arena
7569            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7570        Some(RecognizeOutcome {
7571            index: next_index,
7572            consumed_eof: false,
7573            alt_number: 0,
7574            member_values,
7575            return_values: BTreeMap::new(),
7576            diagnostics,
7577            decisions: Vec::new(),
7578            actions: Vec::new(),
7579            nodes,
7580        })
7581    }
7582
7583    /// Adapts the optional recovery result to the normal outcome list used by
7584    /// rule-call transitions.
7585    fn child_rule_failure_recovery_outcomes(
7586        &mut self,
7587        request: ChildRuleFailureRecovery<'_>,
7588    ) -> Vec<RecognizeOutcome> {
7589        let sync_symbols =
7590            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
7591        self.child_rule_failure_recovery(
7592            request.rule_index,
7593            request.start_index,
7594            &sync_symbols,
7595            request.member_values,
7596            request.expected,
7597        )
7598        .into_iter()
7599        .collect()
7600    }
7601
7602    /// Formats expected token types using ANTLR's single-token or set syntax.
7603    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
7604        expected_symbols_display(symbols, self.vocabulary())
7605    }
7606
7607    /// Returns the single-token deletion repair if the token after `index`
7608    /// satisfies the failed consuming transition.
7609    fn single_token_deletion(
7610        &mut self,
7611        transition: ParserTransition<'_>,
7612        index: usize,
7613        max_token_type: i32,
7614        expected_symbols: &BTreeSet<i32>,
7615    ) -> Option<(ParserDiagnostic, usize, i32)> {
7616        let current_symbol = self.token_type_at(index);
7617        if current_symbol == TOKEN_EOF {
7618            return None;
7619        }
7620        let next_index = self.consume_index(index, current_symbol);
7621        if next_index == index {
7622            return None;
7623        }
7624        let next_symbol = self.token_type_at(next_index);
7625        if !transition.matches(next_symbol, 1, max_token_type) {
7626            return None;
7627        }
7628        let transition_expected = transition_expected_symbols(transition, max_token_type);
7629        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7630            &transition_expected
7631        } else {
7632            expected_symbols
7633        });
7634        let current = self.token_at(index);
7635        let message = format!(
7636            "extraneous input {} expecting {expected_display}",
7637            current
7638                .as_ref()
7639                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7640        );
7641        Some((
7642            diagnostic_for_token(current, message),
7643            next_index,
7644            next_symbol,
7645        ))
7646    }
7647
7648    /// Returns the repair used when deleting the current token lets a recovery
7649    /// state continue with the following token.
7650    fn current_token_deletion(
7651        &mut self,
7652        index: usize,
7653        expected_symbols: &BTreeSet<i32>,
7654    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
7655        if expected_symbols.is_empty() {
7656            return None;
7657        }
7658        let current_symbol = self.token_type_at(index);
7659        if current_symbol == TOKEN_EOF {
7660            return None;
7661        }
7662        let current = self.token_at(index);
7663        let message = format!(
7664            "extraneous input {} expecting {}",
7665            current
7666                .as_ref()
7667                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7668            self.expected_symbols_display(expected_symbols)
7669        );
7670        let diagnostic = diagnostic_for_token(current, message);
7671        let mut skipped = Vec::new();
7672        let mut cursor = index;
7673        loop {
7674            let symbol = self.token_type_at(cursor);
7675            if symbol == TOKEN_EOF {
7676                return None;
7677            }
7678            skipped.push(cursor);
7679            let next_index = self.consume_index(cursor, symbol);
7680            if next_index == cursor {
7681                return None;
7682            }
7683            let next_symbol = self.token_type_at(next_index);
7684            if expected_symbols.contains(&next_symbol) {
7685                return Some((diagnostic, next_index, skipped));
7686            }
7687            cursor = next_index;
7688        }
7689    }
7690
7691    /// Returns the single-token insertion repair for a failed consuming
7692    /// transition. The caller validates the repair by continuing from the
7693    /// transition target at the same input index.
7694    fn single_token_insertion(
7695        &mut self,
7696        transition: ParserTransition<'_>,
7697        index: usize,
7698        max_token_type: i32,
7699        expected_symbols: &BTreeSet<i32>,
7700        follow_symbols: &BTreeSet<i32>,
7701    ) -> Option<(ParserDiagnostic, i32, String)> {
7702        let current_symbol = self.token_type_at(index);
7703        if !follow_symbols.contains(&current_symbol) {
7704            return None;
7705        }
7706        let transition_expected = transition_expected_symbols(transition, max_token_type);
7707        let token_type = transition_expected.iter().next().copied()?;
7708        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7709            &transition_expected
7710        } else {
7711            expected_symbols
7712        });
7713        let mut token_symbols = BTreeSet::new();
7714        token_symbols.insert(token_type);
7715        let missing_token_display = self.expected_symbols_display(&token_symbols);
7716        let current = self.token_at(index);
7717        let message = format!(
7718            "missing {expected_display} at {}",
7719            current
7720                .as_ref()
7721                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7722        );
7723        let text = format!("<missing {missing_token_display}>");
7724        Some((
7725            diagnostic_for_token(current.as_ref(), message),
7726            token_type,
7727            text,
7728        ))
7729    }
7730
7731    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
7732    /// skip the unexpected current token when the following token satisfies the
7733    /// transition that failed.
7734    fn fast_single_token_deletion_recovery(
7735        &mut self,
7736        recovery: FastRecoveryRequest<'_, '_>,
7737        predicate_context: Option<FastPredicateContext<'_>>,
7738    ) -> Vec<FastRecognizeOutcome> {
7739        let FastRecoveryRequest {
7740            atn,
7741            transition,
7742            expected_symbols,
7743            target,
7744            request,
7745            visiting,
7746            memo,
7747            expected,
7748        } = recovery;
7749        let FastRecognizeRequest {
7750            stop_state,
7751            index,
7752            rule_start_index,
7753            decision_start_index,
7754            precedence,
7755            depth,
7756            ..
7757        } = request;
7758        let Some((diagnostic, next_index, next_symbol)) =
7759            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7760        else {
7761            return Vec::new();
7762        };
7763        let after_next = self.consume_index(next_index, next_symbol);
7764        let empty_recovery = self.empty_recovery_symbols();
7765        self.recognize_state_fast(
7766            atn,
7767            FastRecognizeRequest {
7768                state_number: target,
7769                stop_state,
7770                index: after_next,
7771                rule_start_index,
7772                decision_start_index,
7773                precedence,
7774                depth: depth + 1,
7775                recovery_symbols: empty_recovery,
7776                recovery_state: None,
7777            },
7778            FastRecognizeScratch {
7779                predicate_context,
7780                visiting,
7781                memo,
7782                expected,
7783                native_depth: 0,
7784            },
7785        )
7786        .into_iter()
7787        .map(|mut outcome| {
7788            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7789            outcome.diagnostics = self
7790                .recognition_arena
7791                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7792            if self.fast_token_nodes_enabled {
7793                let token = self.arena_token_node(next_index, false);
7794                self.defer_fast_outcome_node(&mut outcome, token);
7795                let error = self.arena_token_node(index, true);
7796                self.defer_fast_outcome_node(&mut outcome, error);
7797            }
7798            outcome
7799        })
7800        .collect()
7801    }
7802
7803    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
7804    /// pretend the expected transition token was present and continue without
7805    /// consuming the current token.
7806    fn fast_single_token_insertion_recovery(
7807        &mut self,
7808        recovery: FastRecoveryRequest<'_, '_>,
7809        predicate_context: Option<FastPredicateContext<'_>>,
7810    ) -> Vec<FastRecognizeOutcome> {
7811        let FastRecoveryRequest {
7812            atn,
7813            transition,
7814            expected_symbols,
7815            target,
7816            request,
7817            visiting,
7818            memo,
7819            expected,
7820        } = recovery;
7821        let FastRecognizeRequest {
7822            stop_state,
7823            index,
7824            rule_start_index,
7825            decision_start_index,
7826            precedence,
7827            depth,
7828            ..
7829        } = request;
7830        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
7831        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7832            transition,
7833            index,
7834            atn.max_token_type(),
7835            &expected_symbols,
7836            &follow_symbols,
7837        ) else {
7838            return Vec::new();
7839        };
7840        let empty_recovery = self.empty_recovery_symbols();
7841        self.recognize_state_fast(
7842            atn,
7843            FastRecognizeRequest {
7844                state_number: target,
7845                stop_state,
7846                index,
7847                rule_start_index,
7848                decision_start_index,
7849                precedence,
7850                depth: depth + 1,
7851                recovery_symbols: empty_recovery,
7852                recovery_state: None,
7853            },
7854            FastRecognizeScratch {
7855                predicate_context,
7856                visiting,
7857                memo,
7858                expected,
7859                native_depth: 0,
7860            },
7861        )
7862        .into_iter()
7863        .map(|mut outcome| {
7864            outcome.diagnostics = self
7865                .recognition_arena
7866                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7867            let missing = self.arena_missing_token_node(token_type, index, text.clone());
7868            self.defer_fast_outcome_node(&mut outcome, missing);
7869            outcome
7870        })
7871        .collect()
7872    }
7873
7874    /// Retries the current fast-recognition state after deleting one
7875    /// unexpected token that precedes a valid loop or block continuation.
7876    fn fast_current_token_deletion_recovery(
7877        &mut self,
7878        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
7879        predicate_context: Option<FastPredicateContext<'_>>,
7880    ) -> Vec<FastRecognizeOutcome> {
7881        let FastCurrentTokenDeletionRequest {
7882            atn,
7883            expected_symbols,
7884            mut request,
7885            visiting,
7886            memo,
7887            expected,
7888        } = recovery;
7889        if request.index == request.rule_start_index {
7890            return Vec::new();
7891        }
7892        let Some((diagnostic, next_index, skipped)) =
7893            self.current_token_deletion(request.index, &expected_symbols)
7894        else {
7895            return Vec::new();
7896        };
7897        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7898        request.index = next_index;
7899        request.depth += 1;
7900        request.recovery_state = None;
7901        self.recognize_state_fast(
7902            atn,
7903            request,
7904            FastRecognizeScratch {
7905                predicate_context,
7906                visiting,
7907                memo,
7908                expected,
7909                native_depth: 0,
7910            },
7911        )
7912        .into_iter()
7913        .map(|mut outcome| {
7914            outcome.diagnostics = self
7915                .recognition_arena
7916                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7917            for index in skipped.iter().rev() {
7918                let error = self.arena_token_node(*index, true);
7919                self.defer_fast_outcome_node(&mut outcome, error);
7920            }
7921            outcome
7922        })
7923        .collect()
7924    }
7925
7926    /// Converts a failed child rule into a recovered fast-recognizer outcome so
7927    /// the parent can keep its child rule context and continue at a sync token.
7928    fn fast_child_rule_failure_recovery(
7929        &mut self,
7930        rule_index: usize,
7931        start_index: usize,
7932        sync_symbols: &BTreeSet<i32>,
7933        expected: &ExpectedTokens,
7934    ) -> Option<FastRecognizeOutcome> {
7935        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7936        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7937        let mut next_index = error_index;
7938        loop {
7939            let symbol = self.token_type_at(next_index);
7940            if sync_symbols.contains(&symbol) {
7941                if next_index == error_index {
7942                    return None;
7943                }
7944                break;
7945            }
7946            if symbol == TOKEN_EOF {
7947                break;
7948            }
7949            let after = self.consume_index(next_index, symbol);
7950            if after == next_index {
7951                break;
7952            }
7953            next_index = after;
7954        }
7955        let diagnostics = self
7956            .recognition_arena
7957            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7958        let mut nodes = NodeSeqId::EMPTY;
7959        if self.fast_token_nodes_enabled {
7960            let error = self.arena_token_node(error_index, true);
7961            self.arena_prepend(&mut nodes, error);
7962        }
7963        Some(FastRecognizeOutcome {
7964            index: next_index,
7965            consumed_eof: false,
7966            diagnostics,
7967            deferred_nodes: FastDeferredNodeId::EMPTY,
7968            nodes,
7969        })
7970    }
7971
7972    /// Adapts the optional child-rule recovery result to the fast-recognizer
7973    /// outcome list used by rule-call transitions.
7974    fn fast_child_rule_failure_recovery_outcomes(
7975        &mut self,
7976        request: FastChildRuleFailureRecoveryRequest<'_>,
7977    ) -> Vec<FastRecognizeOutcome> {
7978        let FastChildRuleFailureRecoveryRequest {
7979            atn,
7980            rule_index,
7981            start_index,
7982            follow_state,
7983            stop_state,
7984            expected,
7985        } = request;
7986        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
7987        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
7988            .into_iter()
7989            .collect()
7990    }
7991
7992    fn defer_fast_outcome_node(
7993        &mut self,
7994        outcome: &mut FastRecognizeOutcome,
7995        node: RecognizedNodeId,
7996    ) {
7997        if outcome.deferred_nodes.is_empty() {
7998            self.arena_prepend(&mut outcome.nodes, node);
7999            return;
8000        }
8001        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
8002        let fragment = self.recognition_arena.deferred_fragment(fragment);
8003        outcome.deferred_nodes = self
8004            .recognition_arena
8005            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
8006    }
8007
8008    fn defer_fast_outcome_alternative(
8009        &mut self,
8010        outcome: &mut FastRecognizeOutcome,
8011        alt_number: usize,
8012    ) {
8013        let alternative = self.recognition_arena.deferred_alternative(alt_number);
8014        outcome.deferred_nodes = self
8015            .recognition_arena
8016            .concat_deferred_nodes(alternative, outcome.deferred_nodes);
8017    }
8018
8019    fn defer_fast_outcome_boundary(
8020        &mut self,
8021        outcome: &mut FastRecognizeOutcome,
8022        rule_index: usize,
8023    ) {
8024        let boundary = self
8025            .recognition_arena
8026            .deferred_left_recursive_boundary(rule_index);
8027        outcome.deferred_nodes = self
8028            .recognition_arena
8029            .concat_deferred_nodes(boundary, outcome.deferred_nodes);
8030    }
8031
8032    fn materialize_fast_deferred_nodes(
8033        &mut self,
8034        root: FastDeferredNodeId,
8035        initial_suffix: NodeSeqId,
8036    ) -> (NodeSeqId, usize) {
8037        if root.is_empty() {
8038            return (initial_suffix, 0);
8039        }
8040
8041        enum Frame {
8042            Visit(FastDeferredNodeId),
8043            ContinuePrefix(FastDeferredNodeId),
8044            FinishRule {
8045                rule: FastDeferredRule,
8046                parent_suffix: NodeSeqId,
8047                parent_alt_number: u32,
8048                parent_pending_boundary: Option<RecognizedNodeId>,
8049            },
8050        }
8051
8052        let mut result = initial_suffix;
8053        // The rope is visited suffix-first while nodes are prepended. Later
8054        // alternatives arrive first, so earlier markers overwrite them; a
8055        // boundary redirects those earlier markers to the wrapped context.
8056        let mut alt_number = 0;
8057        let mut pending_boundary = None;
8058        let mut pending = Vec::with_capacity(16);
8059        pending.push(Frame::Visit(root));
8060        let mut fragment_nodes = Vec::new();
8061        while let Some(frame) = pending.pop() {
8062            match frame {
8063                Frame::Visit(deferred) => {
8064                    if deferred.is_empty() {
8065                        continue;
8066                    }
8067
8068                    match self.recognition_arena.deferred_node(deferred) {
8069                        FastDeferredNode::Fragment(sequence) => {
8070                            fragment_nodes.clear();
8071                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
8072                            while let Some(node) = fragment_nodes.pop() {
8073                                self.arena_prepend(&mut result, node);
8074                            }
8075                        }
8076                        FastDeferredNode::Rule(rule) => {
8077                            let rule = self.recognition_arena.deferred_rule(rule);
8078                            let parent_suffix = result;
8079                            let parent_alt_number = alt_number;
8080                            let parent_pending_boundary = pending_boundary;
8081                            result = rule.children;
8082                            alt_number = 0;
8083                            pending_boundary = None;
8084                            pending.push(Frame::FinishRule {
8085                                rule,
8086                                parent_suffix,
8087                                parent_alt_number,
8088                                parent_pending_boundary,
8089                            });
8090                            pending.push(Frame::Visit(rule.deferred_children));
8091                        }
8092                        FastDeferredNode::Alternative(selected) => {
8093                            if let Some(boundary) = pending_boundary {
8094                                self.recognition_arena
8095                                    .set_boundary_alt_number(boundary, selected);
8096                            } else {
8097                                alt_number = selected;
8098                            }
8099                        }
8100                        FastDeferredNode::LeftRecursiveBoundary { rule_index } => {
8101                            let boundary = self.arena_boundary_node(rule_index as usize, 0);
8102                            self.arena_prepend(&mut result, boundary);
8103                            pending_boundary = Some(boundary);
8104                        }
8105                        FastDeferredNode::Concat {
8106                            prefix,
8107                            suffix: deferred_suffix,
8108                        } => {
8109                            pending.push(Frame::ContinuePrefix(prefix));
8110                            pending.push(Frame::Visit(deferred_suffix));
8111                        }
8112                    }
8113                }
8114                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
8115                Frame::FinishRule {
8116                    rule,
8117                    parent_suffix,
8118                    parent_alt_number,
8119                    parent_pending_boundary,
8120                } => {
8121                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
8122                        rule_index: rule.rule_index,
8123                        invoking_state: rule.invoking_state,
8124                        alt_number,
8125                        start_index: rule.start_index,
8126                        stop_index: rule.stop_index,
8127                        return_values: None,
8128                        children: result,
8129                    });
8130                    result = parent_suffix;
8131                    self.arena_prepend(&mut result, node);
8132                    alt_number = parent_alt_number;
8133                    pending_boundary = parent_pending_boundary;
8134                }
8135            }
8136        }
8137        (result, alt_number as usize)
8138    }
8139
8140    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) -> usize {
8141        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
8142        let (nodes, alt_number) =
8143            self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
8144        outcome.nodes = nodes;
8145        alt_number
8146    }
8147
8148    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
8149    /// heap work instead of the native call stack.
8150    fn recognize_repetition_fast(
8151        &mut self,
8152        atn: &Atn,
8153        request: &FastRecognizeRequest,
8154        shape: FastRepetitionShape,
8155        scratch: FastRecognizeScratch<'_, '_>,
8156    ) -> Vec<FastRecognizeOutcome> {
8157        let FastRecognizeScratch {
8158            predicate_context,
8159            visiting,
8160            memo,
8161            expected,
8162            native_depth,
8163        } = scratch;
8164        let lookahead = if self.fast_first_set_prefilter {
8165            atn.state(request.state_number).and_then(|state| {
8166                state
8167                    .rule_index()
8168                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8169                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
8170            })
8171        } else {
8172            None
8173        };
8174        let (enter_alt_number, exit_alt_number) = if self.fast_track_alt_numbers {
8175            let state = atn
8176                .state(request.state_number)
8177                .expect("repetition request state must exist");
8178            (
8179                next_alt_number(state, 2, shape.enter_transition_index, 0, true),
8180                next_alt_number(state, 2, shape.exit_transition_index, 0, true),
8181            )
8182        } else {
8183            (0, 0)
8184        };
8185        let mut work = Vec::with_capacity(2);
8186        push_fast_repetition_work(
8187            &mut work,
8188            shape,
8189            FastRepetitionPath {
8190                index: request.index,
8191                deferred_nodes: FastDeferredNodeId::EMPTY,
8192                diagnostics: DiagnosticSeqId::EMPTY,
8193                consumed_eof: false,
8194            },
8195            lookahead.as_deref(),
8196            self.token_type_at(request.index),
8197        );
8198        let mut coordinates = FastRepetitionCoordinates::new(request.index);
8199        let mut outcomes = Vec::new();
8200        while let Some(item) = work.pop() {
8201            match item {
8202                FastRepetitionWork::Enter(path) => {
8203                    if !coordinates.insert_entered(path) {
8204                        continue;
8205                    }
8206                    let path_nodes = if enter_alt_number == 0 {
8207                        path.deferred_nodes
8208                    } else {
8209                        let alternative = self
8210                            .recognition_arena
8211                            .deferred_alternative(enter_alt_number);
8212                        self.recognition_arena
8213                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8214                    };
8215                    let body_outcomes = self.recognize_state_fast(
8216                        atn,
8217                        FastRecognizeRequest {
8218                            state_number: shape.enter_target,
8219                            stop_state: shape.body_stop_state,
8220                            index: path.index,
8221                            rule_start_index: request.rule_start_index,
8222                            decision_start_index: request.decision_start_index,
8223                            precedence: request.precedence,
8224                            depth: request.depth.saturating_add(1),
8225                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8226                            recovery_state: request.recovery_state,
8227                        },
8228                        FastRecognizeScratch {
8229                            predicate_context,
8230                            visiting: &mut *visiting,
8231                            memo: &mut *memo,
8232                            expected: &mut *expected,
8233                            native_depth: native_depth + 1,
8234                        },
8235                    );
8236                    for body in body_outcomes.into_iter().rev() {
8237                        // ANTLR rejects nullable repetition bodies. Keep the
8238                        // interpreter bounded for malformed or recovered ATNs
8239                        // by mirroring the existing same-coordinate cycle cut.
8240                        if body.index <= path.index {
8241                            continue;
8242                        }
8243                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
8244                        let body_nodes = self
8245                            .recognition_arena
8246                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
8247                        let deferred_nodes = self
8248                            .recognition_arena
8249                            .concat_deferred_nodes(path_nodes, body_nodes);
8250                        let next_path = FastRepetitionPath {
8251                            index: body.index,
8252                            deferred_nodes,
8253                            diagnostics: self
8254                                .recognition_arena
8255                                .concat_diagnostics(path.diagnostics, body.diagnostics),
8256                            consumed_eof: path.consumed_eof || body.consumed_eof,
8257                        };
8258                        let symbol = self.token_type_at(next_path.index);
8259                        push_fast_repetition_work(
8260                            &mut work,
8261                            shape,
8262                            next_path,
8263                            lookahead.as_deref(),
8264                            symbol,
8265                        );
8266                    }
8267                }
8268                FastRepetitionWork::Exit(path) => {
8269                    if !coordinates.insert_exited(path) {
8270                        continue;
8271                    }
8272                    let path_nodes = if exit_alt_number == 0 {
8273                        path.deferred_nodes
8274                    } else {
8275                        let alternative =
8276                            self.recognition_arena.deferred_alternative(exit_alt_number);
8277                        self.recognition_arena
8278                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8279                    };
8280                    let suffixes = self.recognize_state_fast(
8281                        atn,
8282                        FastRecognizeRequest {
8283                            state_number: shape.exit_target,
8284                            stop_state: request.stop_state,
8285                            index: path.index,
8286                            rule_start_index: request.rule_start_index,
8287                            decision_start_index: request.decision_start_index,
8288                            precedence: request.precedence,
8289                            depth: request.depth.saturating_add(1),
8290                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8291                            recovery_state: request.recovery_state,
8292                        },
8293                        FastRecognizeScratch {
8294                            predicate_context,
8295                            visiting: &mut *visiting,
8296                            memo: &mut *memo,
8297                            expected: &mut *expected,
8298                            native_depth: native_depth + 1,
8299                        },
8300                    );
8301                    for mut outcome in suffixes {
8302                        outcome.deferred_nodes = self
8303                            .recognition_arena
8304                            .concat_deferred_nodes(path_nodes, outcome.deferred_nodes);
8305                        outcome.diagnostics = self
8306                            .recognition_arena
8307                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8308                        outcome.consumed_eof |= path.consumed_eof;
8309                        outcomes.push(outcome);
8310                    }
8311                }
8312            }
8313        }
8314        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8315        outcomes
8316    }
8317
8318    /// Attempts to reach `stop_state` from `state_number` without committing
8319    /// token consumption to the parser's public stream position.
8320    fn recognize_state_fast(
8321        &mut self,
8322        atn: &Atn,
8323        request: FastRecognizeRequest,
8324        scratch: FastRecognizeScratch<'_, '_>,
8325    ) -> Vec<FastRecognizeOutcome> {
8326        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
8327            return self.recognize_state_fast_inner(atn, request, scratch);
8328        }
8329        self.recognize_state_fast_checked(atn, request, scratch)
8330    }
8331
8332    #[inline(never)]
8333    fn recognize_state_fast_checked(
8334        &mut self,
8335        atn: &Atn,
8336        request: FastRecognizeRequest,
8337        mut scratch: FastRecognizeScratch<'_, '_>,
8338    ) -> Vec<FastRecognizeOutcome> {
8339        scratch.native_depth = 1;
8340        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
8341            self.recognize_state_fast_inner(atn, request, scratch)
8342        })
8343    }
8344
8345    #[allow(clippy::too_many_lines)]
8346    fn recognize_state_fast_inner(
8347        &mut self,
8348        atn: &Atn,
8349        request: FastRecognizeRequest,
8350        scratch: FastRecognizeScratch<'_, '_>,
8351    ) -> Vec<FastRecognizeOutcome> {
8352        #[cfg(feature = "perf-counters")]
8353        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8354        let FastRecognizeScratch {
8355            predicate_context,
8356            visiting,
8357            memo,
8358            expected,
8359            native_depth,
8360        } = scratch;
8361        let FastRecognizeRequest {
8362            mut state_number,
8363            stop_state,
8364            mut index,
8365            rule_start_index,
8366            decision_start_index,
8367            precedence,
8368            mut depth,
8369            recovery_symbols,
8370            recovery_state,
8371        } = request;
8372        let max_token_type = atn.max_token_type();
8373        // Walk straight-line epsilon chains in a loop instead of recursing
8374        // into `recognize_state_fast` for each intermediate state. ATN
8375        // serialization places long sequences of `BasicBlock` epsilon
8376        // transitions between decisions: turning that chain into a loop
8377        // collapses many recursive calls (and their memo lookups, vec
8378        // allocations, and visit-set churn) into a single function frame.
8379        // The loop exits as soon as we hit the original state's logic
8380        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8381        // precedence) so existing fanout, recovery, and memoization still
8382        // apply unchanged.
8383        //
8384        // The inline case also handles single-atom-match states on the
8385        // happy-pass path: when the lone consuming transition matches the
8386        // current lookahead, advance the index and continue without paying
8387        // for a full `recognize_state_fast` recursion. We track tokens we
8388        // consumed inline in `inline_consumed_tokens` so they can be
8389        // prepended onto the eventual outcome list once we hit a state
8390        // whose handling falls outside this fast loop.
8391        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8392        let mut inline_consumed_eof = false;
8393        loop {
8394            if depth > RECOGNITION_DEPTH_LIMIT {
8395                return Vec::new();
8396            }
8397            if state_number == stop_state {
8398                let mut nodes = NodeSeqId::EMPTY;
8399                if self.fast_token_nodes_enabled {
8400                    for token_index in inline_consumed_tokens.iter().rev() {
8401                        let token = self.arena_token_node(*token_index, false);
8402                        self.arena_prepend(&mut nodes, token);
8403                    }
8404                }
8405                return vec![FastRecognizeOutcome {
8406                    index,
8407                    consumed_eof: inline_consumed_eof,
8408                    diagnostics: DiagnosticSeqId::EMPTY,
8409                    deferred_nodes: FastDeferredNodeId::EMPTY,
8410                    nodes,
8411                }];
8412            }
8413            let Some(state) = atn.state(state_number) else {
8414                return Vec::new();
8415            };
8416            let transitions = state.transitions();
8417            if transitions.len() == 1 && !state.precedence_rule_decision() {
8418                let transition = transitions
8419                    .first()
8420                    .expect("single transition checked above");
8421                let transition_kind = transition.kind();
8422                let target = transition.target();
8423                match transition_kind {
8424                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
8425                        if left_recursive_boundary(atn, state, target).is_none() =>
8426                    {
8427                        #[cfg(feature = "perf-counters")]
8428                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8429                        state_number = target;
8430                        depth += 1;
8431                        continue;
8432                    }
8433                    ParserTransitionKind::Predicate
8434                        if left_recursive_boundary(atn, state, target).is_none() =>
8435                    {
8436                        #[cfg(feature = "perf-counters")]
8437                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8438                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
8439                        {
8440                            record_predicate_no_viable(expected, decision_start_index, index);
8441                            return Vec::new();
8442                        }
8443                        state_number = target;
8444                        depth += 1;
8445                        continue;
8446                    }
8447                    ParserTransitionKind::Precedence
8448                        if packed_i32(transition.arg0()) >= precedence
8449                            && left_recursive_boundary(atn, state, target).is_none() =>
8450                    {
8451                        #[cfg(feature = "perf-counters")]
8452                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8453                        state_number = target;
8454                        depth += 1;
8455                        continue;
8456                    }
8457                    // Single-atom / range / set / wildcard / not-set states
8458                    // are common (~17K of ~125K calls on C#) and almost
8459                    // always succeed in pass 1: no fanout, no recovery, no
8460                    // diagnostics. Inline the token match and continue
8461                    // walking instead of recursing — the recursive path
8462                    // would just allocate a Vec, build one outcome, prepend
8463                    // a Token node, and return. Skip pass 2 (recovery
8464                    // enabled): there the failure branch matters and the
8465                    // existing recursive code records expected symbols.
8466                    ParserTransitionKind::Atom
8467                    | ParserTransitionKind::Range
8468                    | ParserTransitionKind::Set
8469                    | ParserTransitionKind::NotSet
8470                    | ParserTransitionKind::Wildcard
8471                        if !self.fast_recovery_enabled =>
8472                    {
8473                        let symbol = self.token_type_at(index);
8474                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8475                            #[cfg(feature = "perf-counters")]
8476                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8477                            if self.fast_token_nodes_enabled {
8478                                inline_consumed_tokens.push(index);
8479                            }
8480                            inline_consumed_eof |= symbol == TOKEN_EOF;
8481                            index = self.consume_index(index, symbol);
8482                            state_number = target;
8483                            depth += 1;
8484                            continue;
8485                        }
8486                        // Fall through to break and let the regular
8487                        // body handle the no-match case (returns empty).
8488                    }
8489                    _ => {}
8490                }
8491            }
8492            break;
8493        }
8494        // If we collected token nodes inline but bail to the recursive
8495        // body (decision state, rule call, etc.), the outcomes returned
8496        // below will need those token nodes prepended.
8497        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
8498        let Some(state) = atn.state(state_number) else {
8499            return Vec::new();
8500        };
8501        let transitions = state.transitions();
8502        let transition_count = transitions.len();
8503        if !self.fast_recovery_enabled
8504            && let Some(shape) = fast_repetition_shape(atn, state)
8505        {
8506            let mut outcomes = self.recognize_repetition_fast(
8507                atn,
8508                &FastRecognizeRequest {
8509                    state_number,
8510                    stop_state,
8511                    index,
8512                    rule_start_index,
8513                    decision_start_index,
8514                    precedence,
8515                    depth,
8516                    recovery_symbols: Rc::clone(&recovery_symbols),
8517                    recovery_state,
8518                },
8519                shape,
8520                FastRecognizeScratch {
8521                    predicate_context,
8522                    visiting: &mut *visiting,
8523                    memo: &mut *memo,
8524                    expected: &mut *expected,
8525                    native_depth: native_depth + 1,
8526                },
8527            );
8528            if inline_pending {
8529                for outcome in &mut outcomes {
8530                    outcome.consumed_eof |= inline_consumed_eof;
8531                    if self.fast_token_nodes_enabled {
8532                        for token_index in inline_consumed_tokens.iter().rev() {
8533                            let token = self.arena_token_node(*token_index, false);
8534                            self.defer_fast_outcome_node(outcome, token);
8535                        }
8536                    }
8537                }
8538            }
8539            return outcomes;
8540        }
8541        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
8542        // fields and the rule/decision boundary indices are pure plumbing —
8543        // they only affect the recovery branch and the no-viable diagnostic
8544        // recording, neither of which fires when recovery is off. Zeroing
8545        // them in the memo key collapses calls that visit the same
8546        // `(state, index)` from different rule-call sites onto one cache
8547        // entry, which is the dominant cost on large grammars (e.g. C#) where
8548        // many rules eventually delegate into the same `expression` /
8549        // `primary_expression` / `type` branches.
8550        let key = if self.fast_recovery_enabled {
8551            FastRecognizeKey {
8552                state_number,
8553                stop_state,
8554                index,
8555                rule_start_index,
8556                decision_start_index,
8557                precedence,
8558                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
8559                recovery_state,
8560            }
8561        } else {
8562            FastRecognizeKey {
8563                state_number,
8564                stop_state,
8565                index,
8566                rule_start_index: 0,
8567                decision_start_index: None,
8568                precedence,
8569                recovery_symbols_id: 0,
8570                recovery_state: None,
8571            }
8572        };
8573        // Once the clean-pass probe has established that coordinates do not
8574        // repeat, stop paying for the full memo table. Recovery always keeps
8575        // memoization because cached failures carry diagnostics, while
8576        // repeat-heavy clean parses promote before reaching sparse mode.
8577        let memo_lookup_enabled = self.fast_recovery_enabled
8578            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
8579        if memo_lookup_enabled {
8580            if let Some(outcomes) = memo.get(&key) {
8581                #[cfg(feature = "perf-counters")]
8582                {
8583                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
8584                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
8585                }
8586                // Materialize a fresh `Vec` from the cached slice; the caller
8587                // mutates per-outcome state (eof flags, prepended nodes) so we
8588                // can't hand them the shared backing.
8589                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
8590                    let inline_eof = inline_consumed_eof;
8591                    let inline_tokens = &inline_consumed_tokens;
8592                    return outcomes
8593                        .iter()
8594                        .copied()
8595                        .map(|mut outcome| {
8596                            if inline_eof {
8597                                outcome.consumed_eof = true;
8598                            }
8599                            if self.fast_token_nodes_enabled {
8600                                for token_index in inline_tokens.iter().rev() {
8601                                    let token = self.arena_token_node(*token_index, false);
8602                                    self.defer_fast_outcome_node(&mut outcome, token);
8603                                }
8604                            }
8605                            outcome
8606                        })
8607                        .collect();
8608                }
8609                return outcomes.to_vec();
8610            }
8611            #[cfg(feature = "perf-counters")]
8612            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
8613        }
8614
8615        // Cycle detection: clean recognition keeps the narrow static cycle
8616        // guard used on hot paths. Recovery needs the broader epsilon-state
8617        // guard because an otherwise non-nullable loop body can recover as an
8618        // empty child at EOF and re-enter the loop at the same token.
8619        let needs_cycle_guard = if self.fast_recovery_enabled {
8620            transitions.iter().any(ParserTransition::is_epsilon)
8621        } else {
8622            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
8623        };
8624        #[cfg(feature = "perf-counters")]
8625        if needs_cycle_guard {
8626            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
8627        } else {
8628            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
8629            match state
8630                .transitions()
8631                .first()
8632                .expect("single-transition path requires one transition")
8633                .data()
8634            {
8635                Transition::Rule { .. } => {
8636                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
8637                }
8638                Transition::Atom { .. }
8639                | Transition::Range { .. }
8640                | Transition::Set { .. }
8641                | Transition::NotSet { .. }
8642                | Transition::Wildcard { .. } => {
8643                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
8644                }
8645                _ => {
8646                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
8647                }
8648            }
8649        }
8650        let has_inserted_cycle_guard = if needs_cycle_guard {
8651            if !visiting.insert(key.clone()) {
8652                #[cfg(feature = "perf-counters")]
8653                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
8654                return Vec::new();
8655            }
8656            true
8657        } else {
8658            false
8659        };
8660        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
8661            Some(index)
8662        } else {
8663            decision_start_index
8664        };
8665        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
8666            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
8667        } else {
8668            (Rc::clone(&recovery_symbols), recovery_state)
8669        };
8670
8671        // Lookahead-based pruning. At a multi-alternative state we cache the
8672        // look-1 set of every outgoing transition; on visit we keep only the
8673        // transitions whose look-1 can accept the current lookahead (or that
8674        // can be reached without consuming and so could legitimately match a
8675        // shorter input). This is the main speedup vs. blind speculative
8676        // recursion: it lets each visit fan out only to the alternatives that
8677        // could possibly contribute a clean parse, mirroring the SLL phase of
8678        // ANTLR's adaptive prediction.
8679        //
8680        // Pruning is skipped at:
8681        //   * rule-start states (a child rule call may need every internal
8682        //     transition to surface single-token recovery diagnostics that
8683        //     ANTLR's reference parser emits at the rule's first consuming
8684        //     transition; the FIRST-set retry path turns the prefilter off
8685        //     entirely so let's keep this lightweight too),
8686        //   * left-recursive precedence loops (the precedence transition's
8687        //     gating is dynamic),
8688        //   * states with too few alternatives to benefit.
8689        let lookahead_filter = if transition_count > 1
8690            && self.fast_first_set_prefilter
8691            && !state.precedence_rule_decision()
8692            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
8693        {
8694            state
8695                .rule_index()
8696                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8697                .map(|rule_stop| {
8698                    let symbol = self.token_type_at(index);
8699                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
8700                    (symbol, entry)
8701                })
8702        } else {
8703            None
8704        };
8705        // LL(1) fast path: when the FIRST sets for the decision are disjoint
8706        // and none is nullable, the lookahead deterministically selects one
8707        // alternative. The recursive recognizer can then commit to that single
8708        // alt without iterating every transition through `should_skip_via_lookahead`
8709        // — saving (transition_count - 1) filter probes per visit.
8710        //
8711        // Result is cached per `(state, lookahead_token)` on the parser
8712        // instance, so subsequent visits skip the FIRST-set scan entirely.
8713        let ll1_only_alt: Option<usize> = if transition_count > 1
8714            && let Some((symbol, entry)) = lookahead_filter.as_ref()
8715        {
8716            let key = (state.state_number(), *symbol);
8717            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
8718                cached
8719            } else {
8720                let result = ll1_unique_alt(entry, *symbol);
8721                self.ll1_decision_cache.insert(key, result);
8722                result
8723            }
8724        } else {
8725            None
8726        };
8727        let lookahead_filter = lookahead_filter.as_ref();
8728        // Pre-size only when we expect at least one outcome to land — most
8729        // single-transition fall-throughs (the loop above didn't catch
8730        // because they're atom/rule/predicate) push at most one entry, so
8731        // reserving one slot avoids a reallocation while keeping the
8732        // unused-slot waste at one element.
8733        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
8734        for (transition_index, transition) in transitions.iter().enumerate() {
8735            if let Some(alt) = ll1_only_alt {
8736                // LL(1) determinism: skip every alt except the chosen one.
8737                if alt != transition_index {
8738                    continue;
8739                }
8740            }
8741            let transition_kind = transition.kind();
8742            if ll1_only_alt.is_none()
8743                && should_skip_via_lookahead(
8744                    transition_kind,
8745                    transition_index,
8746                    lookahead_filter,
8747                    index,
8748                    self.fast_recovery_enabled,
8749                    expected,
8750                )
8751            {
8752                continue;
8753            }
8754            let target = transition.target();
8755            let outcomes_before_transition = outcomes.len();
8756            let left_recursive_boundary = match transition_kind {
8757                ParserTransitionKind::Epsilon
8758                | ParserTransitionKind::Action
8759                | ParserTransitionKind::Predicate
8760                | ParserTransitionKind::Precedence => left_recursive_boundary(atn, state, target),
8761                ParserTransitionKind::Atom
8762                | ParserTransitionKind::Range
8763                | ParserTransitionKind::Set
8764                | ParserTransitionKind::NotSet
8765                | ParserTransitionKind::Wildcard
8766                | ParserTransitionKind::Rule => None,
8767            };
8768            match transition_kind {
8769                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
8770                    #[cfg(feature = "perf-counters")]
8771                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8772                    outcomes.extend(self.recognize_state_fast(
8773                        atn,
8774                        FastRecognizeRequest {
8775                            state_number: target,
8776                            stop_state,
8777                            index,
8778                            rule_start_index,
8779                            decision_start_index: next_decision_start_index,
8780                            precedence,
8781                            depth: depth + 1,
8782                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8783                            recovery_state: epsilon_recovery_state,
8784                        },
8785                        FastRecognizeScratch {
8786                            predicate_context,
8787                            visiting,
8788                            memo,
8789                            expected,
8790                            native_depth: native_depth + 1,
8791                        },
8792                    ));
8793                }
8794                ParserTransitionKind::Predicate => {
8795                    #[cfg(feature = "perf-counters")]
8796                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8797                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
8798                        outcomes.extend(self.recognize_state_fast(
8799                            atn,
8800                            FastRecognizeRequest {
8801                                state_number: target,
8802                                stop_state,
8803                                index,
8804                                rule_start_index,
8805                                decision_start_index: next_decision_start_index,
8806                                precedence,
8807                                depth: depth + 1,
8808                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8809                                recovery_state: epsilon_recovery_state,
8810                            },
8811                            FastRecognizeScratch {
8812                                predicate_context,
8813                                visiting,
8814                                memo,
8815                                expected,
8816                                native_depth: native_depth + 1,
8817                            },
8818                        ));
8819                    } else {
8820                        record_predicate_no_viable(expected, next_decision_start_index, index);
8821                    }
8822                }
8823                ParserTransitionKind::Precedence => {
8824                    let transition_precedence = packed_i32(transition.arg0());
8825                    if transition_precedence >= precedence {
8826                        outcomes.extend(self.recognize_state_fast(
8827                            atn,
8828                            FastRecognizeRequest {
8829                                state_number: target,
8830                                stop_state,
8831                                index,
8832                                rule_start_index,
8833                                decision_start_index: next_decision_start_index,
8834                                precedence,
8835                                depth: depth + 1,
8836                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8837                                recovery_state: epsilon_recovery_state,
8838                            },
8839                            FastRecognizeScratch {
8840                                predicate_context,
8841                                visiting,
8842                                memo,
8843                                expected,
8844                                native_depth: native_depth + 1,
8845                            },
8846                        ));
8847                    }
8848                }
8849                ParserTransitionKind::Rule => {
8850                    let rule_index = transition.arg0() as usize;
8851                    let follow_state = transition.arg1() as usize;
8852                    let rule_precedence = packed_i32(transition.arg2());
8853                    #[cfg(feature = "perf-counters")]
8854                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
8855                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
8856                        continue;
8857                    };
8858                    // Lookahead-based pruning. The recognizer would otherwise
8859                    // explore every speculative rule call, producing exponential
8860                    // work on grammars with many epsilon-reachable rules. When
8861                    // the rule is non-nullable and its FIRST set excludes the
8862                    // current lookahead, recursion can't find a clean path
8863                    // *through this rule*. Skipping is only safe if some sibling
8864                    // transition can still consume the lookahead — otherwise the
8865                    // rule call is the sole continuation and must run so the
8866                    // single-token insertion / deletion recovery inside the
8867                    // called rule can fire (mirroring ANTLR's reference behavior
8868                    // of conjuring a missing token at child-rule entry).
8869                    let symbol = self.token_type_at(index);
8870                    if self.fast_first_set_prefilter {
8871                        // Probe the shared cross-parse cache first; build
8872                        // the entry on miss and intern it there. The
8873                        // computation is purely a function of the ATN, so
8874                        // the cached entry is reused across parses (and
8875                        // freshly-instantiated parser values that share
8876                        // the same `&'static Atn`).
8877                        //
8878                        // `rule_first_set` returns the computed entry
8879                        // directly — it intentionally skips inserting into
8880                        // the cache when the FIRST-set walk hit a cycle, so
8881                        // we cannot assume the entry is in the cache after
8882                        // computing it.
8883                        let first = self.cached_rule_first_set(atn, target, child_stop);
8884                        if should_skip_rule_via_first_set(
8885                            &first,
8886                            symbol,
8887                            self.fast_recovery_enabled,
8888                            index,
8889                            expected,
8890                        ) {
8891                            continue;
8892                        }
8893                    }
8894                    let expected_before_child =
8895                        self.fast_recovery_enabled.then(|| expected.clone());
8896                    let mut children = self.recognize_state_fast(
8897                        atn,
8898                        FastRecognizeRequest {
8899                            state_number: target,
8900                            stop_state: child_stop,
8901                            index,
8902                            rule_start_index: index,
8903                            decision_start_index: None,
8904                            precedence: rule_precedence,
8905                            depth: depth + 1,
8906                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8907                            recovery_state: epsilon_recovery_state,
8908                        },
8909                        FastRecognizeScratch {
8910                            predicate_context,
8911                            visiting,
8912                            memo,
8913                            expected,
8914                            native_depth: native_depth + 1,
8915                        },
8916                    );
8917                    if children.is_empty() && self.fast_recovery_enabled {
8918                        children = self.fast_child_rule_failure_recovery_outcomes(
8919                            FastChildRuleFailureRecoveryRequest {
8920                                atn,
8921                                rule_index,
8922                                start_index: index,
8923                                follow_state,
8924                                stop_state,
8925                                expected,
8926                            },
8927                        );
8928                    }
8929                    if let Some(expected_before_child) = expected_before_child {
8930                        if children
8931                            .iter()
8932                            .any(|child| child.diagnostics.is_empty() && child.index > index)
8933                        {
8934                            *expected = expected_before_child;
8935                        }
8936                    }
8937                    for child in children {
8938                        let child_index = child.index;
8939                        let child_consumed_eof = child.consumed_eof;
8940                        let child_diagnostics = child.diagnostics;
8941                        let empty_recovery = self.empty_recovery_symbols();
8942                        let follow_outcomes = self.recognize_state_fast(
8943                            atn,
8944                            FastRecognizeRequest {
8945                                state_number: follow_state,
8946                                stop_state,
8947                                index: child_index,
8948                                rule_start_index,
8949                                decision_start_index: next_decision_start_index,
8950                                precedence,
8951                                depth: depth + 1,
8952                                recovery_symbols: empty_recovery,
8953                                recovery_state: None,
8954                            },
8955                            FastRecognizeScratch {
8956                                predicate_context,
8957                                visiting,
8958                                memo,
8959                                expected,
8960                                native_depth: native_depth + 1,
8961                            },
8962                        );
8963                        if follow_outcomes.is_empty() {
8964                            continue;
8965                        }
8966                        let child_stop_index =
8967                            self.rule_stop_token_index(child_index, child_consumed_eof);
8968                        let child_node = self.build_parse_trees.then(|| {
8969                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
8970                                rule_index: u32::try_from(rule_index)
8971                                    .expect("rule index fits in u32"),
8972                                invoking_state: i32::try_from(invoking_state_number(state_number))
8973                                    .expect("invoking state fits in i32"),
8974                                start_index: u32::try_from(index)
8975                                    .expect("rule start index fits in u32"),
8976                                stop_index: child_stop_index.map(|stop_index| {
8977                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
8978                                }),
8979                                deferred_children: child.deferred_nodes,
8980                                children: child.nodes,
8981                            })
8982                        });
8983                        let child_diags_empty = child_diagnostics.is_empty();
8984                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
8985                            outcome.consumed_eof |= child_consumed_eof;
8986                            // Skip the prepend dance when there's nothing to
8987                            // merge from the child — common case in pass 1.
8988                            if !child_diags_empty {
8989                                outcome.diagnostics = self
8990                                    .recognition_arena
8991                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
8992                            }
8993                            if let Some(child_node) = child_node {
8994                                outcome.deferred_nodes = self
8995                                    .recognition_arena
8996                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
8997                            }
8998                            outcome
8999                        }));
9000                    }
9001                }
9002                ParserTransitionKind::Atom
9003                | ParserTransitionKind::Range
9004                | ParserTransitionKind::Set
9005                | ParserTransitionKind::NotSet
9006                | ParserTransitionKind::Wildcard => {
9007                    #[cfg(feature = "perf-counters")]
9008                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9009                    let symbol = self.token_type_at(index);
9010                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9011                        let next_index = self.consume_index(index, symbol);
9012                        let empty_recovery = self.empty_recovery_symbols();
9013                        outcomes.extend(
9014                            self.recognize_state_fast(
9015                                atn,
9016                                FastRecognizeRequest {
9017                                    state_number: target,
9018                                    stop_state,
9019                                    index: next_index,
9020                                    rule_start_index,
9021                                    decision_start_index: next_decision_start_index,
9022                                    precedence,
9023                                    depth: depth + 1,
9024                                    recovery_symbols: empty_recovery,
9025                                    recovery_state: None,
9026                                },
9027                                FastRecognizeScratch {
9028                                    predicate_context,
9029                                    visiting,
9030                                    memo,
9031                                    expected,
9032                                    native_depth: native_depth + 1,
9033                                },
9034                            )
9035                            .into_iter()
9036                            .map(|mut outcome| {
9037                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9038                                if self.fast_token_nodes_enabled {
9039                                    let token = self.arena_token_node(index, false);
9040                                    self.defer_fast_outcome_node(&mut outcome, token);
9041                                }
9042                                outcome
9043                            }),
9044                        );
9045                    } else {
9046                        if !self.fast_recovery_enabled {
9047                            // In pass 1 there is no recovery to attempt; the
9048                            // recovery branch below would never run, and the
9049                            // `expected_symbols` computation is just there
9050                            // to gate that branch. Skipping it eliminates
9051                            // ~1× `state_expected_symbols` lookup per failed
9052                            // atom transition (≈82K on mono-statement.cs)
9053                            // for zero observable behavior change.
9054                            continue;
9055                        }
9056                        let expected_symbols = fast_recovery_expected_symbols(
9057                            self,
9058                            atn,
9059                            state.state_number(),
9060                            &recovery_symbols,
9061                        );
9062                        if expected_symbols.contains(&symbol) {
9063                            continue;
9064                        }
9065                        {
9066                            expected.record_transition(index, transition, max_token_type);
9067                            record_no_viable_if_ambiguous(
9068                                expected,
9069                                next_decision_start_index,
9070                                index,
9071                            );
9072                            outcomes.extend(self.fast_single_token_deletion_recovery(
9073                                FastRecoveryRequest {
9074                                    atn,
9075                                    transition,
9076                                    expected_symbols: Rc::clone(&expected_symbols),
9077                                    target,
9078                                    request: FastRecognizeRequest {
9079                                        state_number,
9080                                        stop_state,
9081                                        index,
9082                                        rule_start_index,
9083                                        decision_start_index,
9084                                        precedence,
9085                                        depth,
9086                                        recovery_symbols: Rc::clone(&recovery_symbols),
9087                                        recovery_state,
9088                                    },
9089                                    visiting,
9090                                    memo,
9091                                    expected,
9092                                },
9093                                predicate_context,
9094                            ));
9095                            if !state_is_left_recursive_rule(atn, state) {
9096                                outcomes.extend(self.fast_single_token_insertion_recovery(
9097                                    FastRecoveryRequest {
9098                                        atn,
9099                                        transition,
9100                                        expected_symbols: Rc::clone(&expected_symbols),
9101                                        target,
9102                                        request: FastRecognizeRequest {
9103                                            state_number,
9104                                            stop_state,
9105                                            index,
9106                                            rule_start_index,
9107                                            decision_start_index,
9108                                            precedence,
9109                                            depth,
9110                                            recovery_symbols: Rc::clone(&recovery_symbols),
9111                                            recovery_state,
9112                                        },
9113                                        visiting,
9114                                        memo,
9115                                        expected,
9116                                    },
9117                                    predicate_context,
9118                                ));
9119                            }
9120                            outcomes.extend(self.fast_current_token_deletion_recovery(
9121                                FastCurrentTokenDeletionRequest {
9122                                    atn,
9123                                    expected_symbols,
9124                                    request: FastRecognizeRequest {
9125                                        state_number,
9126                                        stop_state,
9127                                        index,
9128                                        rule_start_index,
9129                                        decision_start_index,
9130                                        precedence,
9131                                        depth,
9132                                        recovery_symbols: Rc::clone(&recovery_symbols),
9133                                        recovery_state,
9134                                    },
9135                                    visiting,
9136                                    memo,
9137                                    expected,
9138                                },
9139                                predicate_context,
9140                            ));
9141                        }
9142                    }
9143                }
9144            }
9145            let alt_number = next_alt_number(
9146                state,
9147                transition_count,
9148                transition_index,
9149                0,
9150                self.fast_track_alt_numbers,
9151            );
9152            if alt_number != 0 || left_recursive_boundary.is_some() {
9153                for outcome in &mut outcomes[outcomes_before_transition..] {
9154                    if alt_number != 0 {
9155                        self.defer_fast_outcome_alternative(outcome, alt_number);
9156                    }
9157                    if let Some(rule_index) = left_recursive_boundary {
9158                        self.defer_fast_outcome_boundary(outcome, rule_index);
9159                    }
9160                }
9161            }
9162        }
9163
9164        if has_inserted_cycle_guard {
9165            visiting.remove(&key);
9166        }
9167        if matches!(
9168            self.prediction_mode,
9169            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9170        ) && self.fast_recovery_enabled
9171        {
9172            // Without recovery enabled every outcome already has empty
9173            // diagnostics, so the discard pass is a no-op — skipping it
9174            // saves an iter+retain on each of the ~1M visits.
9175            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
9176        }
9177        if self.fast_recovery_enabled {
9178            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
9179        } else {
9180            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
9181        }
9182        // Skip memoization for single-transition states whose outcome is
9183        // unambiguous: they only get re-entered if the caller revisits the
9184        // exact same call site, which is rare since the loop above already
9185        // collapsed straight-line epsilon walks. Multi-alternative states
9186        // are where backtracking actually revisits the same coordinate, so
9187        // we still memoize there. With recovery on we keep the existing
9188        // memoization unconditionally because the recovery branch may
9189        // record diagnostics that the cache must surface to repeated
9190        // failed visits.
9191        let should_memoize = self.fast_recovery_enabled
9192            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
9193        // Apply inline pending state to each outcome before returning.
9194        // Tokens consumed inline by the loop-collapse don't appear in the
9195        // recursive recognizer's output, so we need to prepend them here.
9196        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
9197            if inline_consumed_eof {
9198                outcome.consumed_eof = true;
9199            }
9200            if !inline_consumed_tokens.is_empty() {
9201                for token_index in inline_consumed_tokens.iter().rev() {
9202                    let token = self.arena_token_node(*token_index, false);
9203                    self.defer_fast_outcome_node(&mut outcome, token);
9204                }
9205            }
9206            outcome
9207        };
9208        if should_memoize {
9209            #[cfg(feature = "perf-counters")]
9210            {
9211                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
9212                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
9213                match outcomes.len() {
9214                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9215                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9216                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9217                }
9218            }
9219            // The memo is keyed by the loop-exit `(state_number, index)` so
9220            // the inline-consumed tokens belong to *this* call's output, not
9221            // the cached result. Memoize the bare outcomes (without the
9222            // inline-pending data), then prepend the inline data on return.
9223            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
9224            memo.insert(key, Rc::clone(&stored));
9225            if inline_pending {
9226                return stored
9227                    .iter()
9228                    .copied()
9229                    .map(&mut apply_inline_pending)
9230                    .collect();
9231            }
9232            return stored.to_vec();
9233        }
9234        #[cfg(feature = "perf-counters")]
9235        match outcomes.len() {
9236            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9237            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9238            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9239        }
9240        if inline_pending {
9241            return outcomes.into_iter().map(apply_inline_pending).collect();
9242        }
9243        outcomes
9244    }
9245
9246    /// Explores single-token deletion recovery while preserving the matched
9247    /// token and skipped error token in the selected parse tree path.
9248    fn single_token_deletion_recovery(
9249        &mut self,
9250        recovery: RecoveryRequest<'_, '_>,
9251    ) -> Vec<RecognizeOutcome> {
9252        let RecoveryRequest {
9253            atn,
9254            transition,
9255            expected_symbols,
9256            target,
9257            request,
9258            visiting,
9259            memo,
9260            expected,
9261        } = recovery;
9262        let RecognizeRequest {
9263            stop_state,
9264            index,
9265            rule_start_index,
9266            decision_start_index,
9267            init_action_rules,
9268            predicates,
9269            semantics,
9270            rule_args,
9271            member_actions,
9272            return_actions,
9273            local_int_arg,
9274            member_values,
9275            return_values,
9276            rule_alt_number,
9277            track_alt_numbers,
9278            consumed_eof,
9279            precedence,
9280            depth,
9281            ..
9282        } = request;
9283        let Some((diagnostic, next_index, next_symbol)) =
9284            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
9285        else {
9286            return Vec::new();
9287        };
9288        let after_next = self.consume_index(next_index, next_symbol);
9289        self.recognize_state(
9290            atn,
9291            RecognizeRequest {
9292                state_number: target,
9293                stop_state,
9294                index: after_next,
9295                rule_start_index,
9296                decision_start_index,
9297                init_action_rules,
9298                predicates,
9299                semantics,
9300                rule_args,
9301                member_actions,
9302                return_actions,
9303                local_int_arg,
9304                member_values,
9305                return_values,
9306                rule_alt_number,
9307                track_alt_numbers,
9308                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
9309                committed_decision: false,
9310                precedence,
9311                depth: depth + 1,
9312                recovery_symbols: BTreeSet::new(),
9313                recovery_state: None,
9314            },
9315            visiting,
9316            memo,
9317            expected,
9318        )
9319        .into_iter()
9320        .map(|mut outcome| {
9321            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9322            outcome.diagnostics = self
9323                .recognition_arena
9324                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9325            let token = self.arena_token_node(next_index, false);
9326            self.arena_prepend(&mut outcome.nodes, token);
9327            let error = self.arena_token_node(index, true);
9328            self.arena_prepend(&mut outcome.nodes, error);
9329            outcome
9330        })
9331        .collect()
9332    }
9333
9334    /// Retries the current recognition state after deleting one unexpected
9335    /// token, preserving the deleted token as an error node in the parse tree.
9336    fn current_token_deletion_recovery(
9337        &mut self,
9338        recovery: CurrentTokenDeletionRequest<'_, '_>,
9339    ) -> Vec<RecognizeOutcome> {
9340        let CurrentTokenDeletionRequest {
9341            atn,
9342            expected_symbols,
9343            mut request,
9344            visiting,
9345            memo,
9346            expected,
9347        } = recovery;
9348        let error_index = request.index;
9349        if error_index == request.rule_start_index {
9350            return Vec::new();
9351        }
9352        let Some((diagnostic, next_index, skipped)) =
9353            self.current_token_deletion(error_index, &expected_symbols)
9354        else {
9355            return Vec::new();
9356        };
9357        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9358        request.index = next_index;
9359        request.committed_decision = false;
9360        request.depth += 1;
9361        request.recovery_state = None;
9362        self.recognize_state(atn, request, visiting, memo, expected)
9363            .into_iter()
9364            .map(|mut outcome| {
9365                outcome.diagnostics = self
9366                    .recognition_arena
9367                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9368                for index in skipped.iter().rev() {
9369                    let error = self.arena_token_node(*index, true);
9370                    self.arena_prepend(&mut outcome.nodes, error);
9371                }
9372                outcome
9373            })
9374            .collect()
9375    }
9376
9377    /// Falls back after deletion/insertion repairs cannot continue from a
9378    /// failed consuming transition.
9379    fn consuming_failure_fallback(
9380        &mut self,
9381        fallback: ConsumingFailureFallback<'_>,
9382        visiting: &mut BTreeSet<RecognizeKey>,
9383        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9384        expected: &mut ExpectedTokens,
9385    ) -> Vec<RecognizeOutcome> {
9386        if fallback.expected_symbols.is_empty() {
9387            return Vec::new();
9388        }
9389        if fallback.symbol == TOKEN_EOF {
9390            return self.eof_consuming_failure_fallback(fallback, expected);
9391        }
9392        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9393    }
9394
9395    /// Keeps unexpected non-EOF input visible as an error node when no repair
9396    /// path can otherwise reach the transition target.
9397    fn non_eof_consuming_failure_fallback(
9398        &mut self,
9399        fallback: ConsumingFailureFallback<'_>,
9400        visiting: &mut BTreeSet<RecognizeKey>,
9401        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9402        expected: &mut ExpectedTokens,
9403    ) -> Vec<RecognizeOutcome> {
9404        let ConsumingFailureFallback {
9405            atn,
9406            target,
9407            request,
9408            symbol,
9409            expected_symbols,
9410            decision_start_index,
9411            decision,
9412        } = fallback;
9413        let error_index = request.index;
9414        let diagnostic =
9415            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
9416        let next_index = self.consume_index(error_index, symbol);
9417        self.recognize_state(
9418            atn,
9419            RecognizeRequest {
9420                state_number: target,
9421                stop_state: request.stop_state,
9422                index: next_index,
9423                rule_start_index: request.rule_start_index,
9424                decision_start_index,
9425                init_action_rules: request.init_action_rules,
9426                predicates: request.predicates,
9427                semantics: request.semantics,
9428                rule_args: request.rule_args,
9429                member_actions: request.member_actions,
9430                return_actions: request.return_actions,
9431                local_int_arg: request.local_int_arg,
9432                member_values: request.member_values,
9433                return_values: request.return_values,
9434                rule_alt_number: request.rule_alt_number,
9435                track_alt_numbers: request.track_alt_numbers,
9436                consumed_eof: request.consumed_eof,
9437                committed_decision: false,
9438                precedence: request.precedence,
9439                depth: request.depth + 1,
9440                recovery_symbols: BTreeSet::new(),
9441                recovery_state: None,
9442            },
9443            visiting,
9444            memo,
9445            expected,
9446        )
9447        .into_iter()
9448        .map(|mut outcome| {
9449            prepend_decision(&mut outcome, decision);
9450            outcome.diagnostics = self
9451                .recognition_arena
9452                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9453            let error = self.arena_token_node(error_index, true);
9454            self.arena_prepend(&mut outcome.nodes, error);
9455            outcome
9456        })
9457        .collect()
9458    }
9459
9460    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
9461    /// behavior of unwinding instead of inserting caller tokens at EOF.
9462    fn eof_consuming_failure_fallback(
9463        &mut self,
9464        fallback: ConsumingFailureFallback<'_>,
9465        expected: &ExpectedTokens,
9466    ) -> Vec<RecognizeOutcome> {
9467        let request = fallback.request;
9468        if request.index == request.rule_start_index {
9469            return Vec::new();
9470        }
9471        let diagnostic =
9472            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
9473        let diagnostics = self
9474            .recognition_arena
9475            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
9476        vec![RecognizeOutcome {
9477            index: request.index,
9478            consumed_eof: request.consumed_eof,
9479            alt_number: request.rule_alt_number,
9480            member_values: request.member_values,
9481            return_values: request.return_values,
9482            diagnostics,
9483            decisions: Vec::new(),
9484            actions: Vec::new(),
9485            nodes: NodeSeqId::EMPTY,
9486        }]
9487    }
9488
9489    /// Explores single-token insertion recovery while adding a conjured
9490    /// missing-token error node to the selected parse tree path.
9491    fn single_token_insertion_recovery(
9492        &mut self,
9493        recovery: RecoveryRequest<'_, '_>,
9494    ) -> Vec<RecognizeOutcome> {
9495        let RecoveryRequest {
9496            atn,
9497            transition,
9498            expected_symbols,
9499            target,
9500            request,
9501            visiting,
9502            memo,
9503            expected,
9504        } = recovery;
9505        let RecognizeRequest {
9506            stop_state,
9507            index,
9508            rule_start_index,
9509            decision_start_index,
9510            init_action_rules,
9511            predicates,
9512            semantics,
9513            rule_args,
9514            member_actions,
9515            return_actions,
9516            local_int_arg,
9517            member_values,
9518            return_values,
9519            rule_alt_number,
9520            track_alt_numbers,
9521            consumed_eof,
9522            precedence,
9523            depth,
9524            ..
9525        } = request;
9526        let follow_symbols = state_expected_symbols(atn, transition.target());
9527        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
9528            transition,
9529            index,
9530            atn.max_token_type(),
9531            &expected_symbols,
9532            &follow_symbols,
9533        ) else {
9534            return Vec::new();
9535        };
9536        self.recognize_state(
9537            atn,
9538            RecognizeRequest {
9539                state_number: target,
9540                stop_state,
9541                index,
9542                rule_start_index,
9543                decision_start_index,
9544                init_action_rules,
9545                predicates,
9546                semantics,
9547                rule_args,
9548                member_actions,
9549                return_actions,
9550                local_int_arg,
9551                member_values,
9552                return_values,
9553                rule_alt_number,
9554                track_alt_numbers,
9555                consumed_eof,
9556                committed_decision: false,
9557                precedence,
9558                depth: depth + 1,
9559                recovery_symbols: BTreeSet::new(),
9560                recovery_state: None,
9561            },
9562            visiting,
9563            memo,
9564            expected,
9565        )
9566        .into_iter()
9567        .map(|mut outcome| {
9568            outcome.diagnostics = self
9569                .recognition_arena
9570                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9571            let missing = self.arena_missing_token_node(token_type, index, text.clone());
9572            self.arena_prepend(&mut outcome.nodes, missing);
9573            outcome
9574        })
9575        .collect()
9576    }
9577
9578    /// Attempts to reach `stop_state` and carries semantic actions for the
9579    /// selected parser path.
9580    #[allow(clippy::too_many_lines)]
9581    fn recognize_state(
9582        &mut self,
9583        atn: &Atn,
9584        request: RecognizeRequest<'_>,
9585        visiting: &mut BTreeSet<RecognizeKey>,
9586        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9587        expected: &mut ExpectedTokens,
9588    ) -> Vec<RecognizeOutcome> {
9589        let request_template = request.clone();
9590        let RecognizeRequest {
9591            state_number,
9592            stop_state,
9593            index,
9594            rule_start_index,
9595            decision_start_index,
9596            init_action_rules,
9597            predicates,
9598            semantics,
9599            rule_args,
9600            member_actions,
9601            return_actions,
9602            local_int_arg,
9603            member_values,
9604            return_values,
9605            rule_alt_number,
9606            track_alt_numbers,
9607            consumed_eof,
9608            committed_decision,
9609            precedence,
9610            depth,
9611            recovery_symbols,
9612            recovery_state,
9613        } = request;
9614        if depth > RECOGNITION_DEPTH_LIMIT {
9615            return Vec::new();
9616        }
9617        if state_number == stop_state {
9618            return stop_outcome(
9619                index,
9620                consumed_eof,
9621                rule_alt_number,
9622                member_values,
9623                return_values,
9624            );
9625        }
9626        let key = RecognizeKey {
9627            state_number,
9628            stop_state,
9629            index,
9630            rule_start_index,
9631            decision_start_index,
9632            local_int_arg,
9633            member_values: member_values.clone(),
9634            return_values: return_values.clone(),
9635            rule_alt_number,
9636            track_alt_numbers,
9637            consumed_eof,
9638            committed_decision,
9639            precedence,
9640            recovery_symbols: recovery_symbols.clone(),
9641            recovery_state,
9642        };
9643        if let Some(outcomes) = memo.get(&key) {
9644            return outcomes.clone();
9645        }
9646
9647        let visit_key = key.clone();
9648        if !visiting.insert(visit_key.clone()) {
9649            return Vec::new();
9650        }
9651
9652        let Some(state) = atn.state(state_number) else {
9653            visiting.remove(&visit_key);
9654            return Vec::new();
9655        };
9656        let decision_override_generation = self.decision_override_generation;
9657        let transitions = state.transitions();
9658        let transition_count = transitions.len();
9659        let overridden_transition = if transition_count > 1
9660            && self.semantic_hooks.observes_parser_decisions()
9661        {
9662            atn.decision_to_state()
9663                .iter()
9664                .position(|candidate| candidate == state_number)
9665                .and_then(|decision| {
9666                    self.semantic_hooks
9667                        .parser_decision_override(decision, index, transition_count)
9668                })
9669                .and_then(|alternative| alternative.checked_sub(1))
9670                .filter(|alternative| *alternative < transition_count)
9671        } else {
9672            None
9673        };
9674        if overridden_transition.is_some() {
9675            self.decision_override_generation = self.decision_override_generation.wrapping_add(1);
9676        }
9677        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9678            Some(index)
9679        } else {
9680            decision_start_index
9681        };
9682        let (epsilon_recovery_symbols, epsilon_recovery_state) =
9683            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
9684        let mut outcomes = Vec::new();
9685        for (transition_index, transition) in transitions.iter().enumerate() {
9686            if overridden_transition.is_some_and(|forced| forced != transition_index) {
9687                continue;
9688            }
9689            let transition_committed =
9690                committed_decision || overridden_transition == Some(transition_index);
9691            let mut transition_request = request_template.clone();
9692            transition_request.committed_decision = transition_committed;
9693            let decision =
9694                transition_decision(atn, state, transition_count, transition_index, predicates);
9695            let next_alt_number = next_alt_number(
9696                state,
9697                transition_count,
9698                transition_index,
9699                rule_alt_number,
9700                track_alt_numbers,
9701            );
9702            let transition_data = transition.data();
9703            match &transition_data {
9704                Transition::Epsilon { target } | Transition::Action { target, .. } => {
9705                    let action_rule_index = match &transition_data {
9706                        Transition::Action { rule_index, .. } => Some(*rule_index),
9707                        _ => None,
9708                    };
9709                    outcomes.extend(self.recognize_epsilon_or_action_step(
9710                        atn,
9711                        &transition_request,
9712                        EpsilonActionStep {
9713                            source_state: state_number,
9714                            target: *target,
9715                            action_rule_index,
9716                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
9717                            decision,
9718                            decision_start_index: next_decision_start_index,
9719                            alt_number: next_alt_number,
9720                            recovery_symbols: epsilon_recovery_symbols.clone(),
9721                            recovery_state: epsilon_recovery_state,
9722                        },
9723                        RecognizeScratch {
9724                            visiting,
9725                            memo,
9726                            expected,
9727                        },
9728                    ));
9729                }
9730                Transition::Predicate {
9731                    target,
9732                    rule_index,
9733                    pred_index,
9734                    ..
9735                } => {
9736                    let predicate = PredicateEval {
9737                        index,
9738                        rule_index: *rule_index,
9739                        pred_index: *pred_index,
9740                        predicates,
9741                        semantics,
9742                        context: None,
9743                        local_int_arg,
9744                        member_values: &member_values,
9745                    };
9746                    if self.parser_predicate_matches(predicate) {
9747                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
9748                        outcomes.extend(
9749                            self.recognize_state(
9750                                atn,
9751                                RecognizeRequest {
9752                                    state_number: *target,
9753                                    stop_state,
9754                                    index,
9755                                    rule_start_index,
9756                                    decision_start_index: next_decision_start_index,
9757                                    init_action_rules,
9758                                    predicates,
9759                                    semantics,
9760                                    rule_args,
9761                                    member_actions,
9762                                    return_actions,
9763                                    local_int_arg,
9764                                    member_values: member_values.clone(),
9765                                    return_values: return_values.clone(),
9766                                    rule_alt_number: next_alt_number,
9767                                    track_alt_numbers,
9768                                    consumed_eof,
9769                                    committed_decision: transition_committed,
9770                                    precedence,
9771                                    depth: depth + 1,
9772                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9773                                    recovery_state: epsilon_recovery_state,
9774                                },
9775                                visiting,
9776                                memo,
9777                                expected,
9778                            )
9779                            .into_iter()
9780                            .map(|mut outcome| {
9781                                prepend_decision(&mut outcome, decision);
9782                                if let Some(rule_index) = left_recursive_boundary {
9783                                    let boundary =
9784                                        self.arena_boundary_node(rule_index, next_alt_number);
9785                                    self.arena_prepend(&mut outcome.nodes, boundary);
9786                                }
9787                                outcome
9788                            }),
9789                        );
9790                    } else if let Some(message) = semantics
9791                        .and_then(|semantics| {
9792                            self.parser_semantic_ir_predicate_failure_message(
9793                                *rule_index,
9794                                *pred_index,
9795                                semantics,
9796                            )
9797                        })
9798                        .or_else(|| {
9799                            self.parser_predicate_failure_message(
9800                                *rule_index,
9801                                *pred_index,
9802                                predicates,
9803                            )
9804                        })
9805                    {
9806                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
9807                            rule_index: *rule_index,
9808                            index,
9809                            message,
9810                            member_values: member_values.clone(),
9811                            return_values: return_values.clone(),
9812                            rule_alt_number,
9813                        }));
9814                    } else {
9815                        record_predicate_no_viable(expected, next_decision_start_index, index);
9816                    }
9817                }
9818                Transition::Precedence {
9819                    target,
9820                    precedence: transition_precedence,
9821                } => {
9822                    if *transition_precedence >= precedence {
9823                        outcomes.extend(
9824                            self.recognize_state(
9825                                atn,
9826                                RecognizeRequest {
9827                                    state_number: *target,
9828                                    stop_state,
9829                                    index,
9830                                    rule_start_index,
9831                                    decision_start_index: next_decision_start_index,
9832                                    init_action_rules,
9833                                    predicates,
9834                                    semantics,
9835                                    rule_args,
9836                                    member_actions,
9837                                    return_actions,
9838                                    local_int_arg,
9839                                    member_values: member_values.clone(),
9840                                    return_values: return_values.clone(),
9841                                    rule_alt_number: next_alt_number,
9842                                    track_alt_numbers,
9843                                    consumed_eof,
9844                                    committed_decision: transition_committed,
9845                                    precedence,
9846                                    depth: depth + 1,
9847                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9848                                    recovery_state: epsilon_recovery_state,
9849                                },
9850                                visiting,
9851                                memo,
9852                                expected,
9853                            )
9854                            .into_iter()
9855                            .map(|mut outcome| {
9856                                prepend_decision(&mut outcome, decision);
9857                                outcome
9858                            }),
9859                        );
9860                    }
9861                }
9862                Transition::Rule {
9863                    target,
9864                    rule_index,
9865                    follow_state,
9866                    precedence: rule_precedence,
9867                    ..
9868                } => {
9869                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
9870                        continue;
9871                    };
9872                    let child_local_int_arg =
9873                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
9874                    let expected_before_child = expected.clone();
9875                    let children = self.recognize_state(
9876                        atn,
9877                        RecognizeRequest {
9878                            state_number: *target,
9879                            stop_state: child_stop,
9880                            index,
9881                            rule_start_index: index,
9882                            decision_start_index: None,
9883                            init_action_rules,
9884                            predicates,
9885                            semantics,
9886                            rule_args,
9887                            member_actions,
9888                            return_actions,
9889                            local_int_arg: child_local_int_arg,
9890                            member_values: member_values.clone(),
9891                            return_values: BTreeMap::new(),
9892                            rule_alt_number: 0,
9893                            track_alt_numbers,
9894                            consumed_eof: false,
9895                            committed_decision: transition_committed,
9896                            precedence: *rule_precedence,
9897                            depth: depth + 1,
9898                            recovery_symbols: epsilon_recovery_symbols.clone(),
9899                            recovery_state: epsilon_recovery_state,
9900                        },
9901                        visiting,
9902                        memo,
9903                        expected,
9904                    );
9905                    let children = if children.is_empty() {
9906                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
9907                            atn,
9908                            rule_index: *rule_index,
9909                            start_index: index,
9910                            follow_state: *follow_state,
9911                            stop_state,
9912                            member_values: member_values.clone(),
9913                            expected,
9914                        })
9915                    } else {
9916                        children
9917                    };
9918                    let preserve_child_expected =
9919                        self.child_expected_reaches_clean_eof(&children, expected);
9920                    restore_expected(
9921                        &children,
9922                        index,
9923                        expected,
9924                        expected_before_child,
9925                        preserve_child_expected,
9926                    );
9927                    for child in children {
9928                        let child_stop_index =
9929                            self.rule_stop_token_index(child.index, child.consumed_eof);
9930                        let child_nodes = self
9931                            .recognition_arena
9932                            .fold_left_recursive_boundaries(child.nodes);
9933                        let child_node = self.arena_rule_node(ArenaRuleSpec {
9934                            rule_index: *rule_index,
9935                            invoking_state: invoking_state_number(state_number),
9936                            alt_number: child.alt_number,
9937                            start_index: index,
9938                            stop_index: child_stop_index,
9939                            return_values: child.return_values.clone(),
9940                            children: child_nodes,
9941                        });
9942                        outcomes.extend(
9943                            self.recognize_state(
9944                                atn,
9945                                RecognizeRequest {
9946                                    state_number: *follow_state,
9947                                    stop_state,
9948                                    index: child.index,
9949                                    rule_start_index,
9950                                    decision_start_index: next_decision_start_index,
9951                                    init_action_rules,
9952                                    predicates,
9953                                    semantics,
9954                                    rule_args,
9955                                    member_actions,
9956                                    return_actions,
9957                                    local_int_arg,
9958                                    member_values: child.member_values.clone(),
9959                                    return_values: return_values.clone(),
9960                                    rule_alt_number,
9961                                    track_alt_numbers,
9962                                    consumed_eof: consumed_eof || child.consumed_eof,
9963                                    committed_decision: transition_committed
9964                                        && child.index == index,
9965                                    precedence,
9966                                    depth: depth + 1,
9967                                    recovery_symbols: BTreeSet::new(),
9968                                    recovery_state: None,
9969                                },
9970                                visiting,
9971                                memo,
9972                                expected,
9973                            )
9974                            .into_iter()
9975                            .map(|mut outcome| {
9976                                outcome.consumed_eof |= child.consumed_eof;
9977                                outcome.diagnostics = self
9978                                    .recognition_arena
9979                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
9980                                let mut decisions = child.decisions.clone();
9981                                decisions.append(&mut outcome.decisions);
9982                                outcome.decisions = decisions;
9983                                prepend_decision(&mut outcome, decision);
9984                                let mut actions = child.actions.clone();
9985                                if init_action_rules.contains(rule_index) {
9986                                    actions.insert(
9987                                        0,
9988                                        ParserAction::new_rule_init(
9989                                            *rule_index,
9990                                            index,
9991                                            Some(*follow_state),
9992                                        ),
9993                                    );
9994                                }
9995                                actions.append(&mut outcome.actions);
9996                                outcome.actions = actions;
9997                                self.arena_prepend(&mut outcome.nodes, child_node);
9998                                outcome
9999                            }),
10000                        );
10001                    }
10002                }
10003                Transition::Atom { target, .. }
10004                | Transition::Range { target, .. }
10005                | Transition::Set { target, .. }
10006                | Transition::NotSet { target, .. }
10007                | Transition::Wildcard { target, .. } => {
10008                    let symbol = self.token_type_at(index);
10009                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
10010                        let next_index = self.consume_index(index, symbol);
10011                        outcomes.extend(
10012                            self.recognize_state(
10013                                atn,
10014                                RecognizeRequest {
10015                                    state_number: *target,
10016                                    stop_state,
10017                                    index: next_index,
10018                                    rule_start_index,
10019                                    decision_start_index: next_decision_start_index,
10020                                    init_action_rules,
10021                                    predicates,
10022                                    semantics,
10023                                    rule_args,
10024                                    member_actions,
10025                                    return_actions,
10026                                    local_int_arg,
10027                                    member_values: member_values.clone(),
10028                                    return_values: return_values.clone(),
10029                                    rule_alt_number: next_alt_number,
10030                                    track_alt_numbers,
10031                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
10032                                    committed_decision: false,
10033                                    precedence,
10034                                    depth: depth + 1,
10035                                    recovery_symbols: BTreeSet::new(),
10036                                    recovery_state: None,
10037                                },
10038                                visiting,
10039                                memo,
10040                                expected,
10041                            )
10042                            .into_iter()
10043                            .map(|mut outcome| {
10044                                prepend_decision(&mut outcome, decision);
10045                                outcome.consumed_eof |= symbol == TOKEN_EOF;
10046                                let token = self.arena_token_node(index, false);
10047                                self.arena_prepend(&mut outcome.nodes, token);
10048                                outcome
10049                            }),
10050                        );
10051                    } else {
10052                        let expected_symbols =
10053                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
10054                        if expected_symbols.contains(&symbol) && !transition_committed {
10055                            continue;
10056                        }
10057                        expected.record_transition(index, transition, atn.max_token_type());
10058                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
10059                        let before_recovery = outcomes.len();
10060                        let recovery_request = transition_request.clone();
10061                        if transition_committed {
10062                            outcomes.extend(self.consuming_failure_fallback(
10063                                ConsumingFailureFallback {
10064                                    atn,
10065                                    target: *target,
10066                                    request: recovery_request,
10067                                    symbol,
10068                                    expected_symbols,
10069                                    decision_start_index: next_decision_start_index,
10070                                    decision,
10071                                },
10072                                visiting,
10073                                memo,
10074                                expected,
10075                            ));
10076                            break;
10077                        }
10078                        outcomes.extend(
10079                            self.single_token_deletion_recovery(RecoveryRequest {
10080                                atn,
10081                                transition,
10082                                expected_symbols: expected_symbols.clone(),
10083                                target: *target,
10084                                request: recovery_request.clone(),
10085                                visiting,
10086                                memo,
10087                                expected,
10088                            })
10089                            .into_iter()
10090                            .map(|mut outcome| {
10091                                prepend_decision(&mut outcome, decision);
10092                                outcome
10093                            }),
10094                        );
10095                        if !state_is_left_recursive_rule(atn, state) {
10096                            outcomes.extend(
10097                                self.single_token_insertion_recovery(RecoveryRequest {
10098                                    atn,
10099                                    transition,
10100                                    expected_symbols: expected_symbols.clone(),
10101                                    target: *target,
10102                                    request: recovery_request.clone(),
10103                                    visiting,
10104                                    memo,
10105                                    expected,
10106                                })
10107                                .into_iter()
10108                                .map(|mut outcome| {
10109                                    prepend_decision(&mut outcome, decision);
10110                                    outcome
10111                                }),
10112                            );
10113                        }
10114                        outcomes.extend(self.current_token_deletion_recovery(
10115                            CurrentTokenDeletionRequest {
10116                                atn,
10117                                expected_symbols: expected_symbols.clone(),
10118                                request: recovery_request.clone(),
10119                                visiting,
10120                                memo,
10121                                expected,
10122                            },
10123                        ));
10124                        if outcomes.len() == before_recovery {
10125                            outcomes.extend(self.consuming_failure_fallback(
10126                                ConsumingFailureFallback {
10127                                    atn,
10128                                    target: *target,
10129                                    request: recovery_request,
10130                                    symbol,
10131                                    expected_symbols,
10132                                    decision_start_index: next_decision_start_index,
10133                                    decision,
10134                                },
10135                                visiting,
10136                                memo,
10137                                expected,
10138                            ));
10139                        }
10140                    }
10141                }
10142            }
10143            if self.decision_override_generation != decision_override_generation {
10144                break;
10145            }
10146        }
10147
10148        visiting.remove(&visit_key);
10149        self.record_prediction_diagnostics(atn, state, index, &outcomes);
10150        if matches!(
10151            self.prediction_mode,
10152            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
10153        ) {
10154            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
10155        }
10156        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
10157        memo.insert(key, outcomes.clone());
10158        outcomes
10159    }
10160
10161    /// Follows an epsilon or semantic-action transition while preserving the
10162    /// path-local side effects that may later become generated action output.
10163    fn recognize_epsilon_or_action_step(
10164        &mut self,
10165        atn: &Atn,
10166        request: &RecognizeRequest<'_>,
10167        step: EpsilonActionStep,
10168        scratch: RecognizeScratch<'_>,
10169    ) -> Vec<RecognizeOutcome> {
10170        let RecognizeScratch {
10171            visiting,
10172            memo,
10173            expected,
10174        } = scratch;
10175        let action = step.action_rule_index.map(|rule_index| {
10176            ParserAction::new(
10177                step.source_state,
10178                rule_index,
10179                request.rule_start_index,
10180                self.rule_stop_token_index(request.index, request.consumed_eof),
10181            )
10182        });
10183        let next_member_values = if action.is_some() {
10184            member_values_after_action(
10185                step.source_state,
10186                request.member_actions,
10187                request.semantics,
10188                &request.member_values,
10189            )
10190        } else {
10191            request.member_values.clone()
10192        };
10193        let next_return_values = action.map_or_else(
10194            || request.return_values.clone(),
10195            |action| {
10196                return_values_after_action(
10197                    step.source_state,
10198                    action.rule_index(),
10199                    request.return_actions,
10200                    request.semantics,
10201                    &request.return_values,
10202                )
10203            },
10204        );
10205
10206        self.recognize_state(
10207            atn,
10208            RecognizeRequest {
10209                state_number: step.target,
10210                stop_state: request.stop_state,
10211                index: request.index,
10212                rule_start_index: request.rule_start_index,
10213                decision_start_index: step.decision_start_index,
10214                init_action_rules: request.init_action_rules,
10215                predicates: request.predicates,
10216                semantics: request.semantics,
10217                rule_args: request.rule_args,
10218                member_actions: request.member_actions,
10219                return_actions: request.return_actions,
10220                local_int_arg: request.local_int_arg,
10221                member_values: next_member_values,
10222                return_values: next_return_values,
10223                rule_alt_number: if step.left_recursive_boundary.is_some() {
10224                    0
10225                } else {
10226                    step.alt_number
10227                },
10228                track_alt_numbers: request.track_alt_numbers,
10229                consumed_eof: request.consumed_eof,
10230                committed_decision: request.committed_decision,
10231                precedence: request.precedence,
10232                depth: request.depth + 1,
10233                recovery_symbols: step.recovery_symbols,
10234                recovery_state: step.recovery_state,
10235            },
10236            visiting,
10237            memo,
10238            expected,
10239        )
10240        .into_iter()
10241        .map(|mut outcome| {
10242            prepend_decision(&mut outcome, step.decision);
10243            if let Some(rule_index) = step.left_recursive_boundary {
10244                let boundary = self.arena_boundary_node(rule_index, step.alt_number);
10245                self.arena_prepend(&mut outcome.nodes, boundary);
10246            }
10247            if let Some(action) = action {
10248                outcome.actions.insert(0, action);
10249            }
10250            outcome
10251        })
10252        .collect()
10253    }
10254
10255    /// Reads the token type at an absolute token-stream index without moving
10256    /// the parser's stream cursor. The fast recognizer probes lookahead at
10257    /// every state visit, so avoiding the seek round-trip is a measurable
10258    /// hot-path win on long inputs.
10259    fn token_type_at(&mut self, index: usize) -> i32 {
10260        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
10261            self.input.fill();
10262        }
10263        self.input.token_type_at_index(index)
10264    }
10265
10266    /// Returns the cached `state_expected_symbols` set for an ATN state.
10267    ///
10268    /// The fast recognizer consults this set on every state visit through
10269    /// `next_recovery_context`; the underlying DFS is a pure function of the
10270    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
10271    ///
10272    /// Caching is layered through `intern_recovery_symbols` so two ATN states
10273    /// with the same expected-symbol set share one `Rc`. That invariant is
10274    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
10275    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
10276    /// always interned before it ends up in a key.
10277    fn cached_state_expected_symbols(
10278        &mut self,
10279        atn: &Atn,
10280        state_number: usize,
10281    ) -> Rc<BTreeSet<i32>> {
10282        if let Some(cached) = self.state_expected_cache.get(&state_number) {
10283            return Rc::clone(cached);
10284        }
10285        let symbols = state_expected_symbols(atn, state_number);
10286        let entry = self.intern_recovery_symbols(symbols);
10287        self.state_expected_cache
10288            .insert(state_number, Rc::clone(&entry));
10289        entry
10290    }
10291
10292    fn cached_state_expected_token_set(
10293        &mut self,
10294        atn: &Atn,
10295        state_number: usize,
10296    ) -> Rc<TokenBitSet> {
10297        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
10298            return Rc::clone(cached);
10299        }
10300        // Purely a function of the ATN, so back the per-parser cache with the
10301        // thread-shared one — fresh parser instances (one per parse in
10302        // generated usage) start warm instead of rewalking the ATN.
10303        let symbols = with_shared_atn_caches(atn, |cache| {
10304            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
10305                return Rc::clone(cached);
10306            }
10307            let symbols = Rc::new(state_expected_token_set(atn, state_number));
10308            cache
10309                .state_expected_tokens
10310                .insert(state_number, Rc::clone(&symbols));
10311            symbols
10312        });
10313        self.state_expected_token_cache
10314            .insert(state_number, Rc::clone(&symbols));
10315        symbols
10316    }
10317
10318    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
10319        if self.rule_stop_reach_cache.len() <= state_number {
10320            self.rule_stop_reach_cache
10321                .resize_with(atn.states().len().max(state_number + 1), || None);
10322        }
10323        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
10324            return reaches;
10325        }
10326        let reaches = with_shared_atn_caches(atn, |cache| {
10327            *cache
10328                .rule_stop_reach
10329                .entry(state_number)
10330                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
10331        });
10332        self.rule_stop_reach_cache[state_number] = Some(reaches);
10333        reaches
10334    }
10335
10336    /// Returns the parser's empty `recovery_symbols` singleton so callers can
10337    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
10338    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
10339        Rc::clone(&self.empty_recovery_symbols)
10340    }
10341
10342    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
10343    /// recognizer can hash and compare keys by pointer.
10344    ///
10345    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
10346    /// come from this method or the empty singleton; otherwise two
10347    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
10348    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
10349    /// recognition coordinates.
10350    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
10351        if set.is_empty() {
10352            return Rc::clone(&self.empty_recovery_symbols);
10353        }
10354        let candidate = Rc::new(set);
10355        match self.recovery_symbols_intern.get(&candidate) {
10356            Some(existing) => Rc::clone(existing),
10357            None => {
10358                self.recovery_symbols_intern
10359                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
10360                candidate
10361            }
10362        }
10363    }
10364
10365    /// Returns the cached look-1 entry for a decision state, computing it on
10366    /// first use. Multi-alternative states are visited many times during
10367    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
10368    /// hash lookup per visit.
10369    fn cached_decision_lookahead(
10370        &mut self,
10371        atn: &Atn,
10372        state: AtnState<'_>,
10373        rule_stop_state: usize,
10374    ) -> Rc<DecisionLookahead> {
10375        // Hit the parser-instance cache first. Decision lookahead is purely
10376        // a function of the ATN/state, so on a warm cache we skip the
10377        // thread-local + RefCell + HashMap-entry dance through
10378        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
10379        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
10380        // hashmap-entry overhead in profiles.
10381        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
10382            return Rc::clone(cached);
10383        }
10384        let entry = with_shared_atn_caches(atn, |cache| {
10385            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10386                return Rc::clone(cached);
10387            }
10388            let mut entry = DecisionLookahead {
10389                transitions: Vec::with_capacity(state.transitions().len()),
10390            };
10391            for transition in &state.transitions() {
10392                entry.transitions.push(transition_first_set(
10393                    atn,
10394                    transition,
10395                    rule_stop_state,
10396                    &mut cache.first_set,
10397                ));
10398            }
10399            let entry = Rc::new(entry);
10400            cache
10401                .decision_lookahead
10402                .insert(state.state_number(), Rc::clone(&entry));
10403            entry
10404        });
10405        self.decision_lookahead_cache
10406            .insert(state.state_number(), Rc::clone(&entry));
10407        entry
10408    }
10409
10410    fn cached_rule_first_set(
10411        &mut self,
10412        atn: &Atn,
10413        target: usize,
10414        child_stop: usize,
10415    ) -> Rc<FirstSet> {
10416        if self.rule_first_set_cache.len() <= target {
10417            self.rule_first_set_cache
10418                .resize_with(atn.states().len().max(target + 1), || None);
10419        }
10420        if let Some(cached) = self
10421            .rule_first_set_cache
10422            .get(target)
10423            .and_then(Option::as_ref)
10424        {
10425            return Rc::clone(cached);
10426        }
10427        let first = with_shared_first_set_cache(atn, |cache| {
10428            rule_first_set(atn, target, child_stop, cache)
10429        });
10430        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
10431        first
10432    }
10433
10434    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
10435        let atn_key = SharedAtnCacheKey::for_atn(atn);
10436        if self.empty_cycle_cache_atn != Some(atn_key) {
10437            self.empty_cycle_cache.clear();
10438            self.empty_cycle_cache_atn = Some(atn_key);
10439        }
10440        if self.empty_cycle_cache.len() <= state_number {
10441            self.empty_cycle_cache
10442                .resize_with(atn.state_count().max(state_number + 1), || None);
10443        }
10444        if let Some(cached) = self.empty_cycle_cache[state_number] {
10445            return cached;
10446        }
10447        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
10448        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
10449        self.empty_cycle_cache[state_number] = Some(result);
10450        result
10451    }
10452
10453    fn empty_path_reaches_state(
10454        &mut self,
10455        atn: &Atn,
10456        state_number: usize,
10457        target_state: usize,
10458        visited: &mut FxHashSet<usize>,
10459    ) -> bool {
10460        enum Work {
10461            Visit(usize),
10462            RuleFollow {
10463                target: usize,
10464                rule_index: usize,
10465                follow_state: usize,
10466            },
10467        }
10468
10469        let mut work = vec![Work::Visit(state_number)];
10470        while let Some(item) = work.pop() {
10471            match item {
10472                Work::Visit(state_number) => {
10473                    if !visited.insert(state_number) {
10474                        continue;
10475                    }
10476                    let Some(state) = atn.state(state_number) else {
10477                        continue;
10478                    };
10479                    let transitions = state.transitions();
10480                    for transition_index in (0..transitions.len()).rev() {
10481                        let transition = transitions
10482                            .get(transition_index)
10483                            .expect("in-bounds parser transition");
10484                        let kind = transition.kind();
10485                        let target = transition.target();
10486                        match kind {
10487                            ParserTransitionKind::Atom
10488                            | ParserTransitionKind::Range
10489                            | ParserTransitionKind::Set
10490                            | ParserTransitionKind::NotSet
10491                            | ParserTransitionKind::Wildcard => {}
10492                            ParserTransitionKind::Rule => {
10493                                if target == target_state {
10494                                    return true;
10495                                }
10496                                work.push(Work::RuleFollow {
10497                                    target,
10498                                    rule_index: transition.arg0() as usize,
10499                                    follow_state: transition.arg1() as usize,
10500                                });
10501                                work.push(Work::Visit(target));
10502                            }
10503                            ParserTransitionKind::Epsilon
10504                            | ParserTransitionKind::Predicate
10505                            | ParserTransitionKind::Action
10506                            | ParserTransitionKind::Precedence => {
10507                                if target == target_state {
10508                                    return true;
10509                                }
10510                                work.push(Work::Visit(target));
10511                            }
10512                        }
10513                    }
10514                }
10515                Work::RuleFollow {
10516                    target,
10517                    rule_index,
10518                    follow_state,
10519                } => {
10520                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
10521                        continue;
10522                    };
10523                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
10524                        if follow_state == target_state {
10525                            return true;
10526                        }
10527                        work.push(Work::Visit(follow_state));
10528                    }
10529                }
10530            }
10531        }
10532        false
10533    }
10534
10535    /// Decides whether the clean recognizer should use its full outcome memo
10536    /// table for this coordinate.
10537    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
10538        match self.clean_memo_mode {
10539            CleanMemoMode::Promote => true,
10540            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
10541            CleanMemoMode::Sparse => {
10542                self.clean_memo_sparse_samples += 1;
10543                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
10544                    return false;
10545                }
10546                self.clean_memo_sparse_samples = 0;
10547                self.clean_memo_mode = CleanMemoMode::Probe;
10548                self.clean_memo_probe_samples = 0;
10549                self.clean_memo_probe_repeats = 0;
10550                self.clean_memo_probe_seen.clear();
10551                self.observe_clean_memo_probe(key)
10552            }
10553        }
10554    }
10555
10556    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
10557        self.clean_memo_probe_samples += 1;
10558        if !self.clean_memo_probe_seen.insert(key.clone()) {
10559            self.clean_memo_probe_repeats += 1;
10560        }
10561        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
10562            self.clean_memo_mode = CleanMemoMode::Promote;
10563            self.clean_memo_probe_seen.clear();
10564            return true;
10565        }
10566        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
10567            self.clean_memo_mode = CleanMemoMode::Sparse;
10568            self.clean_memo_sparse_samples = 0;
10569            self.clean_memo_probe_seen.clear();
10570            return false;
10571        }
10572        true
10573    }
10574
10575    /// Borrows the visible token at an absolute token-stream index.
10576    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
10577        self.input.get(index)
10578    }
10579
10580    /// Returns the compact token ID at an absolute token-stream index.
10581    fn token_id_at(&self, index: usize) -> Option<TokenId> {
10582        self.input.get_id(index)
10583    }
10584
10585    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
10586        let token = self
10587            .token_id_at(index)
10588            .expect("recognized token index must exist in the token store");
10589        let node = if error {
10590            ArenaRecognizedNode::ErrorToken { token }
10591        } else {
10592            ArenaRecognizedNode::Token { token }
10593        };
10594        self.recognition_arena.push_node(node)
10595    }
10596
10597    fn arena_missing_token_node(
10598        &mut self,
10599        token_type: i32,
10600        at_index: usize,
10601        text: String,
10602    ) -> RecognizedNodeId {
10603        let extra = self
10604            .recognition_arena
10605            .push_extra(RecognitionExtra::MissingToken {
10606                token_type,
10607                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
10608                text,
10609            });
10610        self.recognition_arena
10611            .push_node(ArenaRecognizedNode::MissingToken { extra })
10612    }
10613
10614    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
10615        let ArenaRuleSpec {
10616            rule_index,
10617            invoking_state,
10618            alt_number,
10619            start_index,
10620            stop_index,
10621            return_values,
10622            children,
10623        } = spec;
10624        let return_values = (!return_values.is_empty()).then(|| {
10625            self.recognition_arena
10626                .push_extra(RecognitionExtra::ReturnValues(return_values))
10627        });
10628        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
10629            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10630            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
10631            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10632            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
10633            stop_index: stop_index
10634                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
10635            return_values,
10636            children,
10637        })
10638    }
10639
10640    fn arena_boundary_node(&mut self, rule_index: usize, alt_number: usize) -> RecognizedNodeId {
10641        self.recognition_arena
10642            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
10643                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10644                alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10645            })
10646    }
10647
10648    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
10649        *sequence = self.recognition_arena.prepend(*sequence, node);
10650    }
10651
10652    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
10653        self.last_recognition_arena_root = root;
10654        self.last_recognition_arena_diagnostics = diagnostics;
10655        #[cfg(feature = "perf-counters")]
10656        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
10657            let stats = self.recognition_arena_stats();
10658            #[allow(clippy::print_stderr)]
10659            {
10660                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
10661                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
10662                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
10663                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
10664                eprintln!("perf recognition_links_total={}", stats.total_links);
10665                eprintln!("perf recognition_links_live={}", stats.live_links);
10666                eprintln!("perf recognition_links_dead={}", stats.dead_links);
10667                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
10668                eprintln!("perf recognition_extras_total={}", stats.total_extras);
10669                eprintln!("perf recognition_extras_live={}", stats.live_extras);
10670                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
10671                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
10672            }
10673        }
10674    }
10675
10676    fn reset_recognition_arena(&mut self) {
10677        self.recognition_arena.reset();
10678        self.last_recognition_arena_root = NodeSeqId::EMPTY;
10679        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
10680    }
10681
10682    /// Normalizes the current token-stream cursor to the next parser-visible
10683    /// token before capturing a rule start boundary.
10684    fn current_visible_index(&mut self) -> usize {
10685        let index = self.input.index();
10686        self.input.seek(index);
10687        self.input.index()
10688    }
10689
10690    /// Reports whether a child rule reached EOF cleanly while also recording
10691    /// an EOF expectation from a longer path inside that child.
10692    fn child_expected_reaches_clean_eof(
10693        &mut self,
10694        children: &[RecognizeOutcome],
10695        expected: &ExpectedTokens,
10696    ) -> bool {
10697        let Some(index) = expected.index else {
10698            return false;
10699        };
10700        self.token_type_at(index) == TOKEN_EOF
10701            && children
10702                .iter()
10703                .any(|child| child.diagnostics.is_empty() && child.index == index)
10704    }
10705
10706    /// Finds the previous token visible to the parser before `index`.
10707    ///
10708    /// The token stream cursor skips hidden-channel tokens, so subtracting one
10709    /// from a visible-token index can point at whitespace. Parser intervals use
10710    /// this helper to stop at the previous visible token while preserving hidden
10711    /// text inside the rendered interval.
10712    fn previous_token_index(&self, index: usize) -> Option<usize> {
10713        self.input.previous_visible_token_index(index)
10714    }
10715
10716    /// Returns the token-stream index used as a rule stop boundary.
10717    ///
10718    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
10719    /// stop at `index` rather than at the previous visible token.
10720    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
10721        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
10722            Some(index)
10723        } else {
10724            self.previous_token_index(index)
10725        }
10726    }
10727
10728    /// Stop-token index for a rule's `@after` action, matching the boundary that
10729    /// `finish_rule` records on the rule context.
10730    ///
10731    /// A rule that matched EOF leaves the cursor parked on the EOF token
10732    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
10733    /// the current index rather than the previous visible token. Without this,
10734    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
10735    /// report the token before EOF (or `None` for empty input), diverging from
10736    /// the rule context that `finish_rule` builds.
10737    ///
10738    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
10739    /// rule ends right before EOF without matching it (the cursor is parked on
10740    /// EOF, but the rule did not consume it). Prefer
10741    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
10742    /// rule context already recorded with the real flag. Kept for callers without
10743    /// the rule tree in hand.
10744    #[must_use]
10745    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
10746        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
10747        self.rule_stop_token_index(current_index, consumed_eof)
10748    }
10749
10750    /// Stop-token index for a rule's `@after` action, taken from the stop token
10751    /// the rule context already recorded.
10752    ///
10753    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
10754    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
10755    /// the rule context — even when the rule ends immediately before EOF without
10756    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
10757    /// to the cursor-based inference only when the tree carries no rule stop.
10758    #[must_use]
10759    pub fn after_action_stop_index_for_tree(
10760        &mut self,
10761        tree: ParseTree,
10762        current_index: usize,
10763    ) -> Option<usize> {
10764        if let Some(stop) = self
10765            .node(tree)
10766            .as_rule()
10767            .and_then(crate::tree::RuleNodeView::stop_id)
10768        {
10769            return Some(stop.index());
10770        }
10771        self.after_action_stop_index(current_index)
10772    }
10773
10774    /// Start-token index for a rule's `@after` action, taken from the start token
10775    /// the rule context already recorded.
10776    ///
10777    /// `enter_rule` sets the rule context start to the first visible token (it
10778    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
10779    /// `$text` in an `@after` action aligned with the rule context — even when the
10780    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
10781    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
10782    /// the tree carries no rule start.
10783    #[must_use]
10784    pub fn after_action_start_index_for_tree(
10785        &self,
10786        tree: ParseTree,
10787        fallback_index: usize,
10788    ) -> usize {
10789        if let Some(start) = self
10790            .node(tree)
10791            .as_rule()
10792            .and_then(crate::tree::RuleNodeView::start_id)
10793        {
10794            return start.index();
10795        }
10796        fallback_index
10797    }
10798
10799    /// Returns the rule stop token for a selected parse path.
10800    ///
10801    /// EOF transitions do not advance the token-stream cursor, so an EOF match
10802    /// must use the current token rather than the previous visible token.
10803    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
10804        self.rule_stop_token_index(index, consumed_eof)
10805            .and_then(|token_index| self.token_id_at(token_index))
10806    }
10807
10808    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
10809    ///
10810    /// Generated Java reports the failed-predicate message at the current
10811    /// lookahead, then consumes until rule recovery can resume. The metadata
10812    /// runtime models the same visible tree shape by keeping skipped tokens as
10813    /// error nodes and returning from the active rule at EOF.
10814    fn predicate_failure_recovery(
10815        &mut self,
10816        request: PredicateFailureRecovery<'_>,
10817    ) -> RecognizeOutcome {
10818        let PredicateFailureRecovery {
10819            rule_index,
10820            index,
10821            message,
10822            member_values,
10823            return_values,
10824            rule_alt_number,
10825        } = request;
10826        let rule_name = self
10827            .rule_names()
10828            .get(rule_index)
10829            .map_or_else(|| rule_index.to_string(), Clone::clone);
10830        let diagnostic = diagnostic_for_token(
10831            self.token_at(index).as_ref(),
10832            format!("rule {rule_name} {message}"),
10833        );
10834        let mut reversed_nodes = NodeSeqId::EMPTY;
10835        let mut next_index = index;
10836        loop {
10837            let symbol = self.token_type_at(next_index);
10838            if symbol == TOKEN_EOF {
10839                break;
10840            }
10841            let error = self.arena_token_node(next_index, true);
10842            self.arena_prepend(&mut reversed_nodes, error);
10843            let after = self.consume_index(next_index, symbol);
10844            if after == next_index {
10845                break;
10846            }
10847            next_index = after;
10848        }
10849        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
10850        let diagnostics = self
10851            .recognition_arena
10852            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10853        RecognizeOutcome {
10854            index: next_index,
10855            consumed_eof: false,
10856            alt_number: rule_alt_number,
10857            member_values,
10858            return_values,
10859            diagnostics,
10860            decisions: Vec::new(),
10861            actions: Vec::new(),
10862            nodes,
10863        }
10864    }
10865
10866    /// Evaluates a user hook for a predicate coordinate that has no generated
10867    /// runtime table entry.
10868    fn parser_semantic_hook_result(
10869        &mut self,
10870        request: ParserSemanticHookRequest<'_>,
10871    ) -> Option<bool> {
10872        let ParserSemanticHookRequest {
10873            index,
10874            rule_index,
10875            pred_index,
10876            context,
10877            local_int_arg,
10878            member_values,
10879        } = request;
10880        let rule_name = self.rule_names().get(rule_index).cloned();
10881        self.input.seek(index);
10882        let input = &mut self.input;
10883        let semantic_hooks = &mut self.semantic_hooks;
10884        let mut ctx = ParserSemCtx {
10885            input,
10886            tree_storage: &self.tree,
10887            rule_index,
10888            coordinate_index: pred_index,
10889            rule_name,
10890            context,
10891            tree: None,
10892            local_int_arg,
10893            member_values,
10894            action: None,
10895        };
10896        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
10897    }
10898
10899    /// Re-inserts unknown-predicate coordinates recorded before a nested
10900    /// interpreted recognition, preserving order and skipping any the nested
10901    /// call already recorded, so a generated parent's fail-loud coordinates
10902    /// survive descending into an interpreted child.
10903    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
10904        if prior.is_empty() {
10905            return;
10906        }
10907        let mut merged = prior;
10908        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
10909            if !merged.contains(&coordinate) {
10910                merged.push(coordinate);
10911            }
10912        }
10913        self.unknown_predicate_hits = merged;
10914    }
10915
10916    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
10917    /// that has no entry in the generated predicate table.
10918    ///
10919    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
10920    /// the guarded path is abandoned; the parse entry surfaces the recorded
10921    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
10922    /// because a parse that consulted an unknown predicate is unreliable no
10923    /// matter which paths were ultimately selected.
10924    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
10925        apply_unknown_predicate_policy(
10926            self.unknown_predicate_policy,
10927            rule_index,
10928            pred_index,
10929            &mut self.unknown_predicate_hits,
10930        )
10931    }
10932
10933    /// Builds the fail-loud error for unknown predicate coordinates recorded
10934    /// by the current parse, if any.
10935    fn unknown_semantic_error(&self) -> Option<AntlrError> {
10936        use std::fmt::Write as _;
10937        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
10938            return None;
10939        }
10940        let mut message = String::new();
10941        for (rule_index, pred_index) in &self.unknown_predicate_hits {
10942            if !message.is_empty() {
10943                message.push_str("; ");
10944            }
10945            let _ = match self.rule_names().get(*rule_index) {
10946                Some(rule_name) => write!(
10947                    message,
10948                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
10949                ),
10950                None => write!(
10951                    message,
10952                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
10953                ),
10954            };
10955        }
10956        for (rule_index, source_state) in &self.unhandled_action_hits {
10957            if !message.is_empty() {
10958                message.push_str("; ");
10959            }
10960            let _ = match self.rule_names().get(*rule_index) {
10961                Some(rule_name) => write!(
10962                    message,
10963                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
10964                ),
10965                None => write!(
10966                    message,
10967                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
10968                ),
10969            };
10970        }
10971        Some(AntlrError::Unsupported(message))
10972    }
10973
10974    /// Evaluates one lowered predicate expression at the requested input
10975    /// position.
10976    ///
10977    /// This sits in the prediction hot loop, so the context borrows the
10978    /// speculative member state read-only and the rule name by reference —
10979    /// no per-evaluation allocation. Only the hook escape path materializes
10980    /// owned copies, and only when a hook is actually consulted.
10981    fn parser_semir_predicate_matches(
10982        &mut self,
10983        semantics: &ParserSemantics,
10984        predicate: &ParserSemanticPredicate,
10985        request: ParserSemanticHookRequest<'_>,
10986    ) -> bool {
10987        self.input.seek(request.index);
10988        let rule_name = self
10989            .data
10990            .rule_names()
10991            .get(request.rule_index)
10992            .map(String::as_str);
10993        let unknown_predicate_policy = self.unknown_predicate_policy;
10994        let mut ctx = ParserSemIrCtx {
10995            input: &mut self.input,
10996            tree_storage: &self.tree,
10997            semantic_hooks: &mut self.semantic_hooks,
10998            rule_index: request.rule_index,
10999            coordinate_index: request.pred_index,
11000            rule_name,
11001            context: request.context,
11002            local_int_arg: request.local_int_arg,
11003            member_values: request.member_values,
11004            invoked_predicates: &mut self.invoked_predicates,
11005            unknown_predicate_policy,
11006            unknown_predicate_hits: &mut self.unknown_predicate_hits,
11007        };
11008        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
11009    }
11010
11011    fn fast_parser_predicate_matches(
11012        &mut self,
11013        context: Option<FastPredicateContext<'_>>,
11014        transition: ParserTransition<'_>,
11015        index: usize,
11016    ) -> bool {
11017        let Some(context) = context else {
11018            return true;
11019        };
11020        let rule_index = transition.arg0() as usize;
11021        let pred_index = transition.arg1() as usize;
11022        let key = (index, rule_index, pred_index);
11023        if let Some(result) = self.fast_predicate_cache.get(&key) {
11024            return *result;
11025        }
11026        let result = self.parser_predicate_matches(PredicateEval {
11027            index,
11028            rule_index,
11029            pred_index,
11030            predicates: context.predicates,
11031            semantics: context.semantics,
11032            context: None,
11033            local_int_arg: None,
11034            member_values: context.member_values,
11035        });
11036        self.fast_predicate_cache.insert(key, result);
11037        result
11038    }
11039
11040    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
11041        let PredicateEval {
11042            index,
11043            rule_index,
11044            pred_index,
11045            predicates,
11046            semantics,
11047            context,
11048            local_int_arg,
11049            member_values,
11050        } = eval;
11051        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
11052            semantics
11053                .predicates
11054                .iter()
11055                .find(|predicate| {
11056                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
11057                })
11058                .map(|predicate| (semantics, predicate))
11059        }) {
11060            return self.parser_semir_predicate_matches(
11061                semantics,
11062                predicate,
11063                ParserSemanticHookRequest {
11064                    index,
11065                    rule_index,
11066                    pred_index,
11067                    context,
11068                    local_int_arg,
11069                    member_values,
11070                },
11071            );
11072        }
11073        let Some((_, _, predicate)) = predicates
11074            .iter()
11075            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
11076        else {
11077            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
11078                index,
11079                rule_index,
11080                pred_index,
11081                context,
11082                local_int_arg,
11083                member_values,
11084            }) {
11085                return result;
11086            }
11087            return self.unknown_predicate_result(rule_index, pred_index);
11088        };
11089        self.input.seek(index);
11090        match predicate {
11091            ParserPredicate::True => true,
11092            ParserPredicate::False => false,
11093            ParserPredicate::FalseWithMessage { .. } => false,
11094            ParserPredicate::Invoke { value } => {
11095                let key = (rule_index, pred_index);
11096                if !self.invoked_predicates.contains(&key) {
11097                    self.invoked_predicates.push(key);
11098                    use std::io::Write as _;
11099                    let mut stdout = std::io::stdout().lock();
11100                    let _ = writeln!(stdout, "eval={value}");
11101                }
11102                *value
11103            }
11104            ParserPredicate::LookaheadTextEquals { offset, text } => self
11105                .input
11106                .lt(*offset)
11107                .is_some_and(|token| Token::text(&token) == Some(*text)),
11108            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
11109                self.la(*offset) != *token_type
11110            }
11111            ParserPredicate::TokenPairAdjacent => {
11112                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
11113                    return false;
11114                };
11115                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
11116                    return false;
11117                };
11118                first + 1 == second
11119            }
11120            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
11121                .and_then(|context| {
11122                    context
11123                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
11124                        .next()
11125                        .map(crate::tree::RuleNodeView::text)
11126                })
11127                .is_none_or(|actual| actual != *text),
11128            ParserPredicate::LocalIntEquals { value } => {
11129                local_int_arg.is_none_or(|(_, actual)| actual == *value)
11130            }
11131            ParserPredicate::LocalIntLessOrEqual { value } => {
11132                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
11133            }
11134            ParserPredicate::MemberModuloEquals {
11135                member,
11136                modulus,
11137                value,
11138                equals,
11139            } => {
11140                if *modulus == 0 {
11141                    return false;
11142                }
11143                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
11144                (actual == *value) == *equals
11145            }
11146            ParserPredicate::MemberEquals {
11147                member,
11148                value,
11149                equals,
11150            } => {
11151                let actual = member_values.get(member).copied().unwrap_or_default();
11152                (actual == *value) == *equals
11153            }
11154        }
11155    }
11156
11157    /// Returns a generated fail-option message for a predicate coordinate.
11158    fn parser_predicate_failure_message(
11159        &self,
11160        rule_index: usize,
11161        pred_index: usize,
11162        predicates: &[(usize, usize, ParserPredicate)],
11163    ) -> Option<&'static str> {
11164        predicates
11165            .iter()
11166            .find_map(|(rule, pred, predicate)| match predicate {
11167                ParserPredicate::FalseWithMessage { message }
11168                    if *rule == rule_index && *pred == pred_index =>
11169                {
11170                    Some(*message)
11171                }
11172                _ => None,
11173            })
11174    }
11175
11176    /// Returns a generated fail-option message for a `SemIR` predicate
11177    /// coordinate.
11178    pub fn parser_semantic_ir_predicate_failure_message(
11179        &self,
11180        rule_index: usize,
11181        pred_index: usize,
11182        semantics: &ParserSemantics,
11183    ) -> Option<&'static str> {
11184        semantics
11185            .predicates
11186            .iter()
11187            .find(|predicate| {
11188                predicate.rule_index == rule_index && predicate.pred_index == pred_index
11189            })
11190            .and_then(|predicate| predicate.failure_message)
11191    }
11192
11193    /// Returns the token-stream index after consuming `symbol` at `index`.
11194    ///
11195    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
11196    /// index stable and rely on `consumed_eof` to record that EOF was matched.
11197    /// The parser's stream cursor is left untouched: speculative recognition
11198    /// reads ahead by absolute index, so paying for `seek` on every visited
11199    /// state would dominate the hot path. Real consumption is committed by
11200    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
11201    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
11202        if symbol == TOKEN_EOF {
11203            return index;
11204        }
11205        self.input.next_visible_after(index)
11206    }
11207
11208    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
11209    /// decision that failed after consuming a shared prefix.
11210    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
11211        let text = display_input_text(&self.input.text(start_index, error_index));
11212        diagnostic_for_token(
11213            self.token_at(error_index).as_ref(),
11214            format!("no viable alternative at input '{text}'"),
11215        )
11216    }
11217
11218    /// Selects the diagnostic for a failed consuming transition after all
11219    /// recovery repairs have been ruled out.
11220    fn recovery_failure_diagnostic(
11221        &self,
11222        index: usize,
11223        decision_start_index: Option<usize>,
11224        expected_symbols: &BTreeSet<i32>,
11225    ) -> ParserDiagnostic {
11226        if expected_symbols.len() > 1 {
11227            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11228                return self.no_viable_alternative(decision_start, index);
11229            }
11230        }
11231        diagnostic_for_token(
11232            self.token_at(index).as_ref(),
11233            format!(
11234                "mismatched input {} expecting {}",
11235                self.token_at(index)
11236                    .as_ref()
11237                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11238                self.expected_symbols_display(expected_symbols)
11239            ),
11240        )
11241    }
11242
11243    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
11244    /// instead of inserting missing tokens in the caller.
11245    fn eof_rule_recovery_diagnostic(
11246        &self,
11247        index: usize,
11248        expected_symbols: &BTreeSet<i32>,
11249        expected: &ExpectedTokens,
11250    ) -> ParserDiagnostic {
11251        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
11252            &expected.symbols
11253        } else {
11254            expected_symbols
11255        };
11256        diagnostic_for_token(
11257            self.token_at(index).as_ref(),
11258            format!(
11259                "mismatched input {} expecting {}",
11260                self.token_at(index)
11261                    .as_ref()
11262                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11263                self.expected_symbols_display(symbols)
11264            ),
11265        )
11266    }
11267
11268    /// Returns token text for a buffered token interval used by generated
11269    /// `$text` actions.
11270    ///
11271    /// ANTLR treats EOF as a range boundary rather than printable input text,
11272    /// even when an action interval explicitly stops at the EOF token.
11273    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
11274        let Some(stop) = stop else {
11275            return String::new();
11276        };
11277        let stop = if self
11278            .token_at(stop)
11279            .is_some_and(|token| token.token_type() == TOKEN_EOF)
11280        {
11281            let Some(previous) = self.previous_token_index(stop) else {
11282                return String::new();
11283            };
11284            previous
11285        } else {
11286            stop
11287        };
11288        self.input.text(start, stop)
11289    }
11290
11291    /// Resets per-parse prediction diagnostics while keeping the parser-level
11292    /// reporting flag configured by generated harness code.
11293    fn clear_prediction_diagnostics(&mut self) {
11294        self.prediction_diagnostics.clear();
11295        self.reported_prediction_diagnostics.clear();
11296    }
11297
11298    /// Drops every per-parse cache that depends on ATN identity or pins
11299    /// recovery-symbol allocations.
11300    ///
11301    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
11302    /// same parser instance can legally be driven against different grammars
11303    /// in sequence. The four caches reset here are keyed by raw ATN
11304    /// coordinates (state numbers, rule indexes) and would silently hand back
11305    /// entries from a previous ATN if reused — pruning lookahead against the
11306    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
11307    /// for the rest of the process. Clearing them on every parse entry keeps
11308    /// the perf wins (caches still amortize within one parse) without making
11309    /// long-lived parsers leak memory or surface stale ATN data:
11310    ///
11311    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
11312    ///   functions of the ATN's state graph.
11313    /// * `state_expected_cache`, `state_expected_token_cache`,
11314    ///   `rule_stop_reach_cache`, and
11315    ///   `recovery_symbols_intern` together form
11316    ///   the identity invariant that lets `FastRecognizeKey` hash
11317    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
11318    ///   so a stale interned `Rc` cannot outlive its map entry.
11319    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
11320    ///   it is retained here and invalidated lazily when the ATN changes.
11321    fn reset_per_parse_caches(&mut self) {
11322        self.rule_first_set_cache.clear();
11323        self.decision_lookahead_cache.clear();
11324        self.ll1_decision_cache.clear();
11325        self.fast_predicate_cache.clear();
11326        self.rule_stop_reach_cache.clear();
11327        self.clean_memo_mode = CleanMemoMode::Probe;
11328        self.clean_memo_probe_seen.clear();
11329        self.clean_memo_probe_samples = 0;
11330        self.clean_memo_probe_repeats = 0;
11331        self.clean_memo_sparse_samples = 0;
11332        self.recovery_symbols_intern.clear();
11333        self.state_expected_cache.clear();
11334        self.state_expected_token_cache.clear();
11335    }
11336
11337    /// Buffers ANTLR-style diagnostic-listener messages for decision states
11338    /// where multiple clean alternatives survive full-context recognition.
11339    fn record_prediction_diagnostics(
11340        &mut self,
11341        atn: &Atn,
11342        state: AtnState<'_>,
11343        start_index: usize,
11344        outcomes: &[RecognizeOutcome],
11345    ) {
11346        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
11347            return;
11348        }
11349        let Some(decision) = atn
11350            .decision_to_state()
11351            .iter()
11352            .position(|state_number| state_number == state.state_number())
11353        else {
11354            return;
11355        };
11356        let Some(rule_index) = state.rule_index() else {
11357            return;
11358        };
11359        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
11360        for outcome in outcomes
11361            .iter()
11362            .filter(|outcome| outcome.diagnostics.is_empty())
11363        {
11364            let Some(alt) = outcome.decisions.first() else {
11365                continue;
11366            };
11367            alts_by_end
11368                .entry(outcome.index)
11369                .or_default()
11370                .insert(alt + 1);
11371        }
11372        let Some((&end_index, ambig_alts)) = alts_by_end
11373            .iter()
11374            .filter(|(_, alts)| alts.len() > 1)
11375            .max_by_key(|(end, _)| *end)
11376        else {
11377            return;
11378        };
11379        let rule_name = self
11380            .rule_names()
11381            .get(rule_index)
11382            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
11383        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
11384        let input = display_input_text(&self.input.text(start_index, stop_index));
11385        let alts = ambig_alts
11386            .iter()
11387            .map(usize::to_string)
11388            .collect::<Vec<_>>()
11389            .join(", ");
11390        let key = (decision, start_index, format!("{alts}:{input}"));
11391        if !self.reported_prediction_diagnostics.insert(key) {
11392            return;
11393        }
11394        let start_diagnostic = diagnostic_for_token(
11395            self.token_at(start_index),
11396            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
11397        );
11398        let stop_diagnostic = diagnostic_for_token(
11399            self.token_at(stop_index),
11400            format!(
11401                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
11402            ),
11403        );
11404        self.prediction_diagnostics.push(start_diagnostic);
11405        self.prediction_diagnostics.push(stop_diagnostic);
11406    }
11407
11408    /// Formats the tokens expected from an ATN state using ANTLR display names.
11409    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11410        expected_symbols_display(
11411            &state_expected_symbols(atn, state_number),
11412            self.vocabulary(),
11413        )
11414    }
11415
11416    /// Expected-token set at the parser's current ATN state — ANTLR's
11417    /// `getExpectedTokens()`. Generated recognizers expose this as
11418    /// `self.expected_tokens()` for embedded test actions
11419    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
11420    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
11421        let state = usize::try_from(self.data().state()).unwrap_or(0);
11422        ExpectedTokenSet {
11423            symbols: state_expected_symbols(atn, state),
11424        }
11425    }
11426
11427    /// Enables the bail error strategy: the first syntax error aborts the
11428    /// parse instead of recovering.
11429    pub const fn set_bail_on_error(&mut self, bail: bool) {
11430        self.bail_on_error = bail;
11431    }
11432
11433    /// Whether the bail error strategy is active.
11434    #[must_use]
11435    pub const fn bail_on_error(&self) -> bool {
11436        self.bail_on_error
11437    }
11438
11439    /// Names of the rules on the live invocation stack, current rule first —
11440    /// ANTLR's `getRuleInvocationStack()`.
11441    pub fn rule_invocation_stack(&self) -> Vec<String> {
11442        self.rule_context_stack
11443            .iter()
11444            .rev()
11445            .map(|frame| {
11446                self.data()
11447                    .rule_names()
11448                    .get(frame.rule_index)
11449                    .cloned()
11450                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
11451            })
11452            .collect()
11453    }
11454
11455    /// Invoking-state chain for the active rule context, current rule first.
11456    ///
11457    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
11458    pub fn active_invocation_states(&self) -> Vec<isize> {
11459        self.rule_context_stack
11460            .iter()
11461            .skip(1)
11462            .rev()
11463            .map(|frame| frame.invoking_state)
11464            .collect()
11465    }
11466
11467    /// Formats a buffered token in ANTLR's diagnostic token display form.
11468    pub fn token_display_at(&self, index: usize) -> Option<String> {
11469        self.token_at(index).map(|token| format!("{token}"))
11470    }
11471}
11472
11473impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
11474where
11475    S: TokenSource,
11476    H: SemanticHooks,
11477{
11478    fn parse_rule(
11479        &mut self,
11480        rule_index: usize,
11481        invoking_state: isize,
11482        precedence: i32,
11483    ) -> DirectAdaptiveParseResult<ParseTree> {
11484        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
11485            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
11486        )?;
11487        let stop_state = self
11488            .atn
11489            .rule_to_stop_state()
11490            .get(rule_index)
11491            .filter(|state| *state != usize::MAX)
11492            .ok_or(DirectAdaptiveParseControl::Fallback(
11493                DirectAdaptiveFallback::MissingAtn,
11494            ))?;
11495        let start_index = self.parser.current_visible_index();
11496        let mut context = ParserRuleContext::new(rule_index, invoking_state);
11497        if let Some(token) = self.parser.token_id_at(start_index) {
11498            self.parser.set_context_start(&mut context, token);
11499        }
11500        let mut state_number = start_state;
11501        let mut consumed_eof = false;
11502        while state_number != stop_state {
11503            self.step()?;
11504            let (transition, boundary) = self.next_transition(state_number, precedence)?;
11505            if boundary.is_some() {
11506                return Err(DirectAdaptiveParseControl::Fallback(
11507                    DirectAdaptiveFallback::LeftRecursiveBoundary,
11508                ));
11509            }
11510            match transition.data() {
11511                Transition::Epsilon { target } => {
11512                    state_number = target;
11513                }
11514                Transition::Precedence {
11515                    target,
11516                    precedence: transition_precedence,
11517                } => {
11518                    if transition_precedence < precedence {
11519                        return Err(DirectAdaptiveParseControl::Fallback(
11520                            DirectAdaptiveFallback::Precedence,
11521                        ));
11522                    }
11523                    state_number = target;
11524                }
11525                Transition::Rule {
11526                    rule_index,
11527                    follow_state,
11528                    precedence: rule_precedence,
11529                    ..
11530                } => {
11531                    let child = self.parse_rule(
11532                        rule_index,
11533                        invoking_state_number(state_number),
11534                        rule_precedence,
11535                    )?;
11536                    if self.parser.build_parse_trees {
11537                        self.parser.tree.add_child(&mut context, child);
11538                    }
11539                    state_number = follow_state;
11540                }
11541                Transition::Atom { .. }
11542                | Transition::Range { .. }
11543                | Transition::Set { .. }
11544                | Transition::NotSet { .. }
11545                | Transition::Wildcard { .. } => {
11546                    let (matched_eof, child) = self.consume_transition(transition)?;
11547                    consumed_eof |= matched_eof;
11548                    if let Some(child) = child {
11549                        self.parser.tree.add_child(&mut context, child);
11550                    }
11551                    state_number = transition.target();
11552                }
11553                Transition::Predicate { .. } => {
11554                    return Err(DirectAdaptiveParseControl::Fallback(
11555                        DirectAdaptiveFallback::Predicate,
11556                    ));
11557                }
11558                Transition::Action { .. } => {
11559                    return Err(DirectAdaptiveParseControl::Fallback(
11560                        DirectAdaptiveFallback::Action,
11561                    ));
11562                }
11563            }
11564        }
11565
11566        let stop_index = self
11567            .parser
11568            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
11569        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
11570            self.parser.set_context_stop(&mut context, token);
11571        }
11572        Ok(self.parser.rule_node(context))
11573    }
11574
11575    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
11576        self.steps += 1;
11577        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
11578            return Err(DirectAdaptiveParseControl::Fallback(
11579                DirectAdaptiveFallback::StepLimit,
11580            ));
11581        }
11582        Ok(())
11583    }
11584
11585    fn next_transition(
11586        &mut self,
11587        state_number: usize,
11588        precedence: i32,
11589    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
11590        let state = self
11591            .atn
11592            .state(state_number)
11593            .ok_or(DirectAdaptiveParseControl::Fallback(
11594                DirectAdaptiveFallback::MissingAtn,
11595            ))?;
11596        if state.is_rule_stop() {
11597            return Err(DirectAdaptiveParseControl::Fallback(
11598                DirectAdaptiveFallback::RuleStop,
11599            ));
11600        }
11601        let transition_index =
11602            self.transition_index(state_number, state.transitions().len(), precedence)?;
11603        let transition = state.transitions().get(transition_index).ok_or(
11604            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
11605        )?;
11606        let boundary = match &transition.data() {
11607            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
11608                left_recursive_boundary(self.atn, state, *target)
11609            }
11610            _ => None,
11611        };
11612        Ok((transition, boundary))
11613    }
11614
11615    fn transition_index(
11616        &mut self,
11617        state_number: usize,
11618        transition_count: usize,
11619        precedence: i32,
11620    ) -> DirectAdaptiveParseResult<usize> {
11621        match transition_count {
11622            0 => Err(DirectAdaptiveParseControl::Fallback(
11623                DirectAdaptiveFallback::NoTransition,
11624            )),
11625            1 => Ok(0),
11626            _ => {
11627                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
11628                    return Ok(alt);
11629                }
11630                let decision = self
11631                    .decision_by_state
11632                    .get(state_number)
11633                    .and_then(|decision| *decision)
11634                    .ok_or(DirectAdaptiveParseControl::Fallback(
11635                        DirectAdaptiveFallback::UnknownDecision,
11636                    ))?;
11637                let prediction = self
11638                    .simulator
11639                    .adaptive_predict_stream_info_with_precedence(
11640                        decision,
11641                        direct_precedence(precedence),
11642                        &mut self.parser.input,
11643                    )
11644                    .map_err(|_| {
11645                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
11646                    })?;
11647                if prediction.has_semantic_context {
11648                    return Err(DirectAdaptiveParseControl::Fallback(
11649                        DirectAdaptiveFallback::SemanticContext,
11650                    ));
11651                }
11652                prediction
11653                    .alt
11654                    .checked_sub(1)
11655                    .filter(|index| *index < transition_count)
11656                    .ok_or(DirectAdaptiveParseControl::Fallback(
11657                        DirectAdaptiveFallback::InvalidAlt,
11658                    ))
11659            }
11660        }
11661    }
11662
11663    fn ll1_transition_index(
11664        &mut self,
11665        state_number: usize,
11666        transition_count: usize,
11667    ) -> DirectAdaptiveParseResult<Option<usize>> {
11668        let state = self
11669            .atn
11670            .state(state_number)
11671            .ok_or(DirectAdaptiveParseControl::Fallback(
11672                DirectAdaptiveFallback::MissingAtn,
11673            ))?;
11674        if state.precedence_rule_decision() {
11675            return Ok(None);
11676        }
11677        let Some(rule_stop) = state
11678            .rule_index()
11679            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
11680        else {
11681            return Ok(None);
11682        };
11683        let symbol = self.parser.input.la_token(1);
11684        let entry = self
11685            .parser
11686            .cached_decision_lookahead(self.atn, state, rule_stop);
11687        Ok(
11688            ll1_greedy_alt(&entry, symbol, state.non_greedy())
11689                .filter(|alt| *alt < transition_count),
11690        )
11691    }
11692
11693    fn consume_transition(
11694        &mut self,
11695        transition: ParserTransition<'_>,
11696    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
11697        let symbol = self.parser.input.la_token(1);
11698        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
11699            return Err(DirectAdaptiveParseControl::Fallback(
11700                DirectAdaptiveFallback::TokenMismatch,
11701            ));
11702        }
11703        let token = self
11704            .parser
11705            .input
11706            .lt_id(1)
11707            .ok_or(DirectAdaptiveParseControl::Fallback(
11708                DirectAdaptiveFallback::TokenMismatch,
11709            ))?;
11710        let matched_eof = symbol == TOKEN_EOF;
11711        if !matched_eof {
11712            self.parser.consume();
11713        }
11714        let child = self
11715            .parser
11716            .build_parse_trees
11717            .then(|| self.parser.terminal_tree(token));
11718        Ok((matched_eof, child))
11719    }
11720}
11721
11722/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
11723/// transformed left-recursive rule.
11724fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
11725    if !state.precedence_rule_decision() {
11726        return None;
11727    }
11728    let target_state = atn.state(target)?;
11729    if target_state.kind() == AtnStateKind::LoopEnd {
11730        return None;
11731    }
11732    state.rule_index()
11733}
11734
11735/// Selects the first outer alternative observed for a rule path.
11736///
11737/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
11738/// outer decision. The metadata recognizer only needs this when a generated
11739/// grammar opts into that target template; otherwise the value remains `0` and
11740/// parse-tree rendering is unchanged.
11741fn next_alt_number(
11742    state: AtnState<'_>,
11743    transition_count: usize,
11744    transition_index: usize,
11745    current_alt_number: usize,
11746    track_alt_numbers: bool,
11747) -> usize {
11748    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
11749        return current_alt_number;
11750    }
11751    if matches!(
11752        state.kind(),
11753        AtnStateKind::Basic
11754            | AtnStateKind::BlockStart
11755            | AtnStateKind::PlusBlockStart
11756            | AtnStateKind::StarBlockStart
11757            | AtnStateKind::StarLoopEntry
11758    ) && !state.precedence_rule_decision()
11759    {
11760        return transition_index + 1;
11761    }
11762    current_alt_number
11763}
11764
11765/// Converts an ATN state number into the signed invoking-state slot used by
11766/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
11767fn invoking_state_number(state_number: usize) -> isize {
11768    isize::try_from(state_number).unwrap_or(isize::MAX)
11769}
11770
11771const fn packed_i32(value: u32) -> i32 {
11772    i32::from_le_bytes(value.to_le_bytes())
11773}
11774
11775fn direct_precedence(precedence: i32) -> usize {
11776    usize::try_from(precedence.max(0)).unwrap_or_default()
11777}
11778
11779fn token_input_display(token: &impl Token) -> String {
11780    format!("'{}'", token.text().unwrap_or("<EOF>"))
11781}
11782
11783fn display_input_text(text: &str) -> String {
11784    let mut out = String::new();
11785    for ch in text.chars() {
11786        match ch {
11787            '\n' => out.push_str("\\n"),
11788            '\r' => out.push_str("\\r"),
11789            '\t' => out.push_str("\\t"),
11790            other => out.push(other),
11791        }
11792    }
11793    out
11794}
11795
11796fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
11797    let (line, column) = token.map_or((0, 0), |token| (token.line(), token.column()));
11798    ParserDiagnostic {
11799        line,
11800        column,
11801        message,
11802    }
11803}
11804
11805fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
11806    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
11807}
11808
11809fn expected_symbols_display_iter(
11810    symbols: impl IntoIterator<Item = i32>,
11811    vocabulary: &Vocabulary,
11812) -> String {
11813    let items = symbols
11814        .into_iter()
11815        .map(|symbol| expected_symbol_display(symbol, vocabulary))
11816        .collect::<Vec<_>>();
11817    if let [single] = items.as_slice() {
11818        return single.clone();
11819    }
11820    format!("{{{}}}", items.join(", "))
11821}
11822
11823fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
11824    if symbol == TOKEN_EOF {
11825        return "<EOF>".to_owned();
11826    }
11827    vocabulary.display_name(symbol)
11828}
11829
11830fn caller_follow_token_info_for_stream<S: TokenSource>(
11831    input: &mut CommonTokenStream<S>,
11832    index: usize,
11833) -> (i32, bool, bool) {
11834    // Generated callers own statement separators; leave them available when
11835    // an interpreted child rule can either stop before or consume one.
11836    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
11837        input.fill();
11838    }
11839    let token_type = input.token_type_at_index(index);
11840    let visible_channel = input.channel();
11841    let token = input.get(index);
11842    let is_boundary = token
11843        .as_ref()
11844        .and_then(Token::text)
11845        .is_some_and(is_caller_follow_boundary_text);
11846    let is_boundary_gap = token.as_ref().is_some_and(|token| {
11847        token.channel() != visible_channel
11848            || is_caller_follow_boundary_gap_text(token.text_or_empty())
11849    });
11850    (token_type, is_boundary, is_boundary_gap)
11851}
11852
11853fn is_caller_follow_boundary_text(text: &str) -> bool {
11854    text.chars().any(|ch| ch == ';' || ch == '\n')
11855        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11856}
11857
11858fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
11859    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11860}
11861
11862/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
11863/// Inline insertion in those precedence loops can synthesize a missing operand
11864/// before an operator and then block the legitimate loop-exit path.
11865fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
11866    let Some(rule_index) = state.rule_index() else {
11867        return false;
11868    };
11869    atn.rule_to_start_state()
11870        .get(rule_index)
11871        .and_then(|state_number| atn.state(state_number))
11872        .is_some_and(AtnState::left_recursive_rule)
11873}
11874
11875/// Picks the better of two `parse_atn_rule` passes (with and without the
11876/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
11877/// recovered one; among recovered outcomes the second pass is preferred
11878/// because the no-prefilter walk reaches ANTLR-style recovery inside child
11879/// rules. If both passes failed, the second pass's expected-token snapshot
11880/// is returned so the caller renders the same diagnostic ANTLR would.
11881fn select_better_top_outcome(
11882    first: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
11883    second: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
11884    arena: &RecognitionArena,
11885) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
11886    match (first, second) {
11887        (Ok(first), Ok(second)) => {
11888            if arena.diagnostics(first.0.diagnostics).next().is_none() {
11889                Ok(first)
11890            } else {
11891                Ok(second)
11892            }
11893        }
11894        (Ok(first), Err(_)) => Ok(first),
11895        (Err(_), Ok(second)) => Ok(second),
11896        (Err(_), Err(second_expected)) => Err(second_expected),
11897    }
11898}
11899
11900/// Chooses the outermost parse result that consumed the most input.
11901///
11902/// The recognizer intentionally keeps shorter endpoints available while walking
11903/// nested rule transitions so callers can satisfy following tokens such as
11904/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
11905fn select_best_fast_outcome(
11906    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
11907    prediction_mode: PredictionMode,
11908    caller_follow: Option<&TokenBitSet>,
11909    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
11910    arena: &RecognitionArena,
11911) -> Option<FastRecognizeOutcome> {
11912    let mut best = None;
11913    let mut best_caller_follow = None;
11914    for outcome in outcomes {
11915        if matches!(
11916            prediction_mode,
11917            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11918        ) && outcome.diagnostics.is_empty()
11919            && let Some(follow) = caller_follow
11920        {
11921            let (token_type, is_boundary, _) = token_info_at(outcome.index);
11922            if is_boundary && follow.contains(token_type) {
11923                let replace =
11924                    best_caller_follow
11925                        .as_ref()
11926                        .is_none_or(|existing: &FastRecognizeOutcome| {
11927                            (outcome.index, outcome.consumed_eof)
11928                                < (existing.index, existing.consumed_eof)
11929                        });
11930                if replace {
11931                    best_caller_follow = Some(outcome);
11932                }
11933            }
11934        }
11935        let Some(existing) = best else {
11936            best = Some(outcome);
11937            continue;
11938        };
11939        let outcome_position = (outcome.index, outcome.consumed_eof);
11940        let best_position = (existing.index, existing.consumed_eof);
11941        let better = match prediction_mode {
11942            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
11943                outcome_position,
11944                outcome.diagnostics,
11945                best_position,
11946                existing.diagnostics,
11947                arena,
11948            ),
11949            PredictionMode::Sll => outcome.index > existing.index,
11950        };
11951        best = Some(if better { outcome } else { existing });
11952    }
11953    let should_use_caller_follow =
11954        best_caller_follow
11955            .as_ref()
11956            .zip(best.as_ref())
11957            .is_some_and(|(candidate, selected)| {
11958                if !selected.diagnostics.is_empty() {
11959                    return true;
11960                }
11961                candidate.index < selected.index
11962                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
11963            });
11964    if should_use_caller_follow {
11965        best_caller_follow
11966    } else {
11967        best
11968    }
11969}
11970
11971fn select_best_outcome(
11972    outcomes: impl Iterator<Item = RecognizeOutcome>,
11973    prediction_mode: PredictionMode,
11974    arena: &RecognitionArena,
11975) -> Option<RecognizeOutcome> {
11976    let outcomes = outcomes.collect::<Vec<_>>();
11977    let prefer_first_tie = outcomes
11978        .iter()
11979        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
11980    outcomes.into_iter().reduce(|best, outcome| {
11981        let outcome_position = (outcome.index, outcome.consumed_eof);
11982        let best_position = (best.index, best.consumed_eof);
11983        let better = match prediction_mode {
11984            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
11985                outcome_is_better(
11986                    outcome_position,
11987                    outcome.diagnostics,
11988                    best_position,
11989                    best.diagnostics,
11990                    arena,
11991                ) || (!prefer_first_tie
11992                    && outcome_position == best_position
11993                    && arena.diagnostics_len(outcome.diagnostics)
11994                        == arena.diagnostics_len(best.diagnostics)
11995                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
11996                        == arena.diagnostics_recovery_rank(best.diagnostics)
11997                    && (outcome.decisions < best.decisions
11998                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
11999            }
12000            PredictionMode::Sll => {
12001                outcome_position > best_position
12002                    || (outcome_position == best_position
12003                        && !prefer_first_tie
12004                        && (outcome.decisions < best.decisions
12005                            || (outcome.decisions == best.decisions
12006                                && outcome_is_better(
12007                                    outcome_position,
12008                                    outcome.diagnostics,
12009                                    best_position,
12010                                    best.diagnostics,
12011                                    arena,
12012                                ))))
12013            }
12014        };
12015        if better {
12016            return outcome;
12017        }
12018        best
12019    })
12020}
12021
12022/// Records the serialized transition order at parser decision states.
12023///
12024/// When two clean paths consume the same input, ANTLR's adaptive prediction
12025/// chooses by alternative order. Keeping this compact trace lets the metadata
12026/// recognizer distinguish greedy and non-greedy optional blocks without a full
12027/// prediction simulator.
12028fn transition_decision(
12029    atn: &Atn,
12030    state: AtnState<'_>,
12031    transition_count: usize,
12032    transition_index: usize,
12033    predicates: &[(usize, usize, ParserPredicate)],
12034) -> Option<usize> {
12035    if transition_count <= 1
12036        || state.precedence_rule_decision()
12037        || decision_reaches_unsupported_predicate(atn, state, predicates)
12038    {
12039        return None;
12040    }
12041    Some(transition_index)
12042}
12043
12044/// Reports whether a state should reset the active no-viable decision start.
12045///
12046/// Loop entry/back states are continuations of the surrounding adaptive
12047/// prediction; resetting at those states would turn LL-star failures back into
12048/// ordinary mismatches.
12049fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
12050    transition_count > 1
12051        && !matches!(
12052            state.kind(),
12053            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
12054        )
12055}
12056
12057/// Marks a farthest expected-token set as no-viable when multiple alternatives
12058/// failed after the active decision had already consumed input.
12059fn record_no_viable_if_ambiguous(
12060    expected: &mut ExpectedTokens,
12061    decision_start_index: Option<usize>,
12062    index: usize,
12063) {
12064    if expected.index == Some(index) && expected.symbols.len() > 1 {
12065        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
12066            expected.record_no_viable(decision_start, index);
12067        }
12068    }
12069}
12070
12071/// Records a no-viable decision caused by a failed semantic predicate before
12072/// any consuming transition can contribute an expected-token set.
12073const fn record_predicate_no_viable(
12074    expected: &mut ExpectedTokens,
12075    decision_start_index: Option<usize>,
12076    index: usize,
12077) {
12078    if let Some(decision_start) = decision_start_index {
12079        expected.record_no_viable(decision_start, index);
12080    }
12081}
12082
12083/// Returns the active decision start only when the error is past that start.
12084const fn no_viable_decision_start(
12085    decision_start_index: Option<usize>,
12086    index: usize,
12087) -> Option<usize> {
12088    match decision_start_index {
12089        Some(start) if index > start => Some(start),
12090        _ => None,
12091    }
12092}
12093
12094/// Restores expected-token bookkeeping when a child rule found a clean
12095/// consuming path; failures in longer child alternatives should not pollute the
12096/// caller's final expectation set.
12097fn restore_expected(
12098    children: &[RecognizeOutcome],
12099    child_start_index: usize,
12100    expected: &mut ExpectedTokens,
12101    snapshot: ExpectedTokens,
12102    preserve_child_expected: bool,
12103) {
12104    if preserve_child_expected {
12105        return;
12106    }
12107    if children
12108        .iter()
12109        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
12110    {
12111        *expected = snapshot;
12112    }
12113}
12114
12115/// Reports whether a decision can reach a predicate the generator did not
12116/// translate. Static alternative order is unsafe for those context predicates.
12117fn decision_reaches_unsupported_predicate(
12118    atn: &Atn,
12119    state: AtnState<'_>,
12120    predicates: &[(usize, usize, ParserPredicate)],
12121) -> bool {
12122    state.transitions().iter().any(|transition| {
12123        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
12124    })
12125}
12126
12127/// Walks epsilon-like edges from one transition to find unsupported predicates.
12128fn transition_reaches_unsupported_predicate(
12129    atn: &Atn,
12130    transition: ParserTransition<'_>,
12131    predicates: &[(usize, usize, ParserPredicate)],
12132    visited: &mut BTreeSet<usize>,
12133) -> bool {
12134    match &transition.data() {
12135        Transition::Predicate {
12136            rule_index,
12137            pred_index,
12138            ..
12139        } => !predicates
12140            .iter()
12141            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
12142        Transition::Epsilon { target }
12143        | Transition::Action { target, .. }
12144        | Transition::Rule { target, .. } => {
12145            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
12146        }
12147        Transition::Precedence { .. }
12148        | Transition::Atom { .. }
12149        | Transition::Range { .. }
12150        | Transition::Set { .. }
12151        | Transition::NotSet { .. }
12152        | Transition::Wildcard { .. } => false,
12153    }
12154}
12155
12156/// Finds an unsupported predicate reachable before a consuming transition.
12157fn state_reaches_unsupported_predicate(
12158    atn: &Atn,
12159    state_number: usize,
12160    predicates: &[(usize, usize, ParserPredicate)],
12161    visited: &mut BTreeSet<usize>,
12162) -> bool {
12163    if !visited.insert(state_number) {
12164        return false;
12165    }
12166    let Some(state) = atn.state(state_number) else {
12167        return false;
12168    };
12169    state.transitions().iter().any(|transition| {
12170        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
12171    })
12172}
12173
12174/// Adds a decision step to the front of an already-recognized suffix path.
12175fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
12176    if let Some(decision) = decision {
12177        outcome.decisions.insert(0, decision);
12178    }
12179}
12180
12181fn outcome_is_better(
12182    outcome_position: (usize, bool),
12183    outcome_diagnostics: DiagnosticSeqId,
12184    best_position: (usize, bool),
12185    best_diagnostics: DiagnosticSeqId,
12186    arena: &RecognitionArena,
12187) -> bool {
12188    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
12189    let best_len = arena.diagnostics_len(best_diagnostics);
12190    outcome_position > best_position
12191        || (outcome_position == best_position
12192            && (outcome_len < best_len
12193                || (outcome_len == best_len
12194                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
12195                        < arena.diagnostics_recovery_rank(best_diagnostics))))
12196}
12197
12198fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
12199    if outcomes
12200        .iter()
12201        .any(|outcome| outcome.diagnostics.is_empty())
12202    {
12203        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12204    }
12205}
12206
12207fn discard_recovered_outcomes_if_clean_path_exists(
12208    outcomes: &mut Vec<RecognizeOutcome>,
12209    arena: &RecognitionArena,
12210) {
12211    if outcomes
12212        .iter()
12213        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
12214    {
12215        return;
12216    }
12217    if outcomes
12218        .iter()
12219        .any(|outcome| outcome.diagnostics.is_empty())
12220    {
12221        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12222    }
12223}
12224
12225/// Reports whether a recovered outcome came from an explicit predicate
12226/// fail-option and therefore should compete with shorter clean loop exits.
12227fn outcome_has_rule_failure_diagnostic(
12228    outcome: &RecognizeOutcome,
12229    arena: &RecognitionArena,
12230) -> bool {
12231    arena
12232        .diagnostics(outcome.diagnostics)
12233        .any(|diagnostic| diagnostic.message.starts_with("rule "))
12234}
12235
12236/// Removes equivalent endpoints before memoizing a state result while
12237/// preserving ATN transition-discovery order.
12238///
12239/// Outcomes are compared on observable recognition state — the input index,
12240/// EOF consumption, and diagnostics — without descending into the parse-tree
12241/// fragment carried by `nodes`. Two paths reaching the same point with
12242/// different node trees would otherwise prevent memoization from collapsing
12243/// equivalent suffixes and explode the speculative-path cache.
12244///
12245/// The first occurrence per recognition key wins, which matches ANTLR's
12246/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
12247/// transitions before loop-exit, so the first-discovered outcome carries the
12248/// greedy parse-tree fragment.
12249fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
12250    if outcomes.len() < 2 {
12251        return;
12252    }
12253    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
12254    outcomes.retain(|outcome| {
12255        seen.insert((
12256            outcome.index,
12257            outcome.consumed_eof,
12258            arena.diagnostics_len(outcome.diagnostics),
12259            arena.diagnostics_recovery_rank(outcome.diagnostics),
12260        ))
12261    });
12262}
12263
12264const FAST_OUTCOME_INLINE_KEYS: usize = 8;
12265const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
12266const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
12267const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
12268
12269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12270enum FastOutcomeDedupStrategy {
12271    Inline,
12272    Dense,
12273    Sparse,
12274}
12275
12276impl FastOutcomeDedupScratch {
12277    fn prepare_dense(&mut self, word_count: usize) {
12278        while let Some(word_index) = self.touched_dense_words.pop() {
12279            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
12280        }
12281        if self.dense_words.len() < word_count {
12282            self.dense_words.resize(word_count, 0);
12283        }
12284    }
12285}
12286
12287fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
12288    let first_index = outcomes.first()?.index;
12289    let (min_index, max_index) = outcomes[1..].iter().fold(
12290        (first_index, first_index),
12291        |(min_index, max_index), outcome| {
12292            (min_index.min(outcome.index), max_index.max(outcome.index))
12293        },
12294    );
12295    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
12296    let bit_count = index_span.checked_mul(2)?;
12297    let word_count =
12298        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
12299    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
12300    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
12301    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
12302        .then_some((min_index, word_count))
12303}
12304
12305#[cfg(feature = "perf-counters")]
12306fn record_clean_fast_outcome_dedup(
12307    strategy: FastOutcomeDedupStrategy,
12308    input_len: usize,
12309    output_len: usize,
12310    dense_words: usize,
12311) {
12312    let counter = match strategy {
12313        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
12314        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
12315        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
12316    };
12317    perf_counters::inc(
12318        &perf_counters::OUTCOME_DEDUPE_INPUTS,
12319        u64::try_from(input_len).unwrap_or(u64::MAX),
12320    );
12321    perf_counters::inc(
12322        &perf_counters::OUTCOME_DEDUPE_REMOVED,
12323        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
12324    );
12325    perf_counters::inc(counter, 1);
12326    perf_counters::inc(
12327        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
12328        u64::try_from(dense_words).unwrap_or(u64::MAX),
12329    );
12330}
12331
12332/// Removes duplicate clean endpoints while preserving transition-discovery
12333/// order. Tiny lists stay on the stack; larger compact ranges use a direct
12334/// bitmap, and only wide sparse ranges pay for hashing.
12335fn dedupe_clean_fast_outcomes(
12336    outcomes: &mut Vec<FastRecognizeOutcome>,
12337    scratch: &mut FastOutcomeDedupScratch,
12338) -> FastOutcomeDedupStrategy {
12339    #[cfg(feature = "perf-counters")]
12340    let input_len = outcomes.len();
12341    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
12342        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
12343        let mut inline_len = 0_usize;
12344        outcomes.retain(|outcome| {
12345            let key = (outcome.index, outcome.consumed_eof);
12346            if inline_keys[..inline_len].contains(&key) {
12347                return false;
12348            }
12349            inline_keys[inline_len] = key;
12350            inline_len += 1;
12351            true
12352        });
12353        #[cfg(feature = "perf-counters")]
12354        record_clean_fast_outcome_dedup(
12355            FastOutcomeDedupStrategy::Inline,
12356            input_len,
12357            outcomes.len(),
12358            0,
12359        );
12360        return FastOutcomeDedupStrategy::Inline;
12361    }
12362
12363    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
12364        scratch.prepare_dense(word_count);
12365        outcomes.retain(|outcome| {
12366            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
12367            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
12368            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
12369            let word = &mut scratch.dense_words[word_index];
12370            if *word & bit != 0 {
12371                return false;
12372            }
12373            if *word == 0 {
12374                scratch
12375                    .touched_dense_words
12376                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
12377            }
12378            *word |= bit;
12379            true
12380        });
12381        #[cfg(feature = "perf-counters")]
12382        record_clean_fast_outcome_dedup(
12383            FastOutcomeDedupStrategy::Dense,
12384            input_len,
12385            outcomes.len(),
12386            word_count,
12387        );
12388        return FastOutcomeDedupStrategy::Dense;
12389    }
12390
12391    scratch.sparse_keys.clear();
12392    scratch.sparse_keys.reserve(outcomes.len());
12393    outcomes.retain(|outcome| {
12394        scratch
12395            .sparse_keys
12396            .insert((outcome.index, outcome.consumed_eof))
12397    });
12398    #[cfg(feature = "perf-counters")]
12399    record_clean_fast_outcome_dedup(
12400        FastOutcomeDedupStrategy::Sparse,
12401        input_len,
12402        outcomes.len(),
12403        0,
12404    );
12405    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
12406        scratch.sparse_keys = FxHashSet::default();
12407    }
12408    FastOutcomeDedupStrategy::Sparse
12409}
12410
12411/// Sorts and removes equivalent endpoints, including action traces and the
12412/// arena-backed node sequence's structural contents.
12413fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
12414    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
12415    outcomes
12416        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
12417}
12418
12419fn compare_recognize_outcomes(
12420    left: &RecognizeOutcome,
12421    right: &RecognizeOutcome,
12422    arena: &RecognitionArena,
12423) -> Ordering {
12424    left.index
12425        .cmp(&right.index)
12426        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
12427        .then_with(|| left.alt_number.cmp(&right.alt_number))
12428        .then_with(|| left.member_values.cmp(&right.member_values))
12429        .then_with(|| left.return_values.cmp(&right.return_values))
12430        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
12431        .then_with(|| left.decisions.cmp(&right.decisions))
12432        .then_with(|| left.actions.cmp(&right.actions))
12433        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
12434}
12435
12436impl<S, H> Recognizer for BaseParser<S, H>
12437where
12438    S: TokenSource,
12439    H: SemanticHooks,
12440{
12441    fn data(&self) -> &RecognizerData {
12442        &self.data
12443    }
12444
12445    fn data_mut(&mut self) -> &mut RecognizerData {
12446        &mut self.data
12447    }
12448}
12449
12450impl<S, H> Parser for BaseParser<S, H>
12451where
12452    S: TokenSource,
12453    H: SemanticHooks,
12454{
12455    fn build_parse_trees(&self) -> bool {
12456        self.build_parse_trees
12457    }
12458
12459    fn set_build_parse_trees(&mut self, build: bool) {
12460        self.build_parse_trees = build;
12461    }
12462
12463    fn number_of_syntax_errors(&self) -> usize {
12464        Self::number_of_syntax_errors(self)
12465    }
12466
12467    fn report_diagnostic_errors(&self) -> bool {
12468        self.report_diagnostic_errors
12469    }
12470
12471    fn set_report_diagnostic_errors(&mut self, report: bool) {
12472        self.report_diagnostic_errors = report;
12473    }
12474
12475    fn prediction_mode(&self) -> PredictionMode {
12476        self.prediction_mode
12477    }
12478
12479    fn set_prediction_mode(&mut self, mode: PredictionMode) {
12480        self.prediction_mode = mode;
12481    }
12482}
12483
12484#[cfg(test)]
12485#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
12486mod tests {
12487    use super::*;
12488    use crate::atn::parser::{
12489        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
12490    };
12491    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
12492    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
12493    use crate::token_stream::CommonTokenStream;
12494    use crate::tree::{NodeKind, ParseTreeStats};
12495    use crate::vocabulary::Vocabulary;
12496    use std::cell::RefCell;
12497    use std::mem::size_of;
12498    use std::rc::Rc;
12499    use std::sync::{Arc, Mutex};
12500
12501    #[test]
12502    fn fx_hasher_write_matches_typed_methods_for_full_words() {
12503        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
12504        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
12505        // fields) must hash the same as the typed methods, otherwise an
12506        // `FxHashMap` keyed on such a type silently disagrees with itself
12507        // depending on which entry point the caller used. Verify the
12508        // little-endian word equivalence this PR established.
12509        let value: u64 = 0x0102_0304_0506_0708;
12510        let mut typed = FxHasher::default();
12511        typed.write_u64(value);
12512        let mut bytewise = FxHasher::default();
12513        bytewise.write(&value.to_le_bytes());
12514        assert_eq!(typed.finish(), bytewise.finish());
12515    }
12516
12517    #[derive(Clone, Debug)]
12518    struct TestToken {
12519        spec: TokenSpec,
12520        id: TokenId,
12521        source_name: String,
12522    }
12523
12524    impl TestToken {
12525        fn new(token_type: i32) -> Self {
12526            Self {
12527                spec: TokenSpec::explicit(token_type, ""),
12528                id: TokenId::try_from(0).expect("zero token ID"),
12529                source_name: String::new(),
12530            }
12531        }
12532
12533        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
12534            Self {
12535                spec: TokenSpec::eof(index, index, line, column),
12536                id: TokenId::try_from(0).expect("zero token ID"),
12537                source_name: source_name.to_owned(),
12538            }
12539        }
12540
12541        fn with_text(mut self, text: impl Into<String>) -> Self {
12542            self.spec.text = Some(text.into());
12543            self
12544        }
12545
12546        const fn with_channel(mut self, channel: i32) -> Self {
12547            self.spec.channel = channel;
12548            self
12549        }
12550
12551        const fn with_span(mut self, start: usize, stop: usize) -> Self {
12552            self.spec.start = start;
12553            self.spec.stop = stop;
12554            self.spec.start_byte = start;
12555            self.spec.stop_byte = match stop.checked_add(1) {
12556                Some(end) if end >= start => end,
12557                Some(_) | None => start,
12558            };
12559            self
12560        }
12561
12562        const fn with_position(mut self, line: usize, column: usize) -> Self {
12563            self.spec.line = line;
12564            self.spec.column = column;
12565            self
12566        }
12567
12568        fn set_token_index(&mut self, index: isize) {
12569            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
12570        }
12571    }
12572
12573    impl Token for TestToken {
12574        fn token_id(&self) -> TokenId {
12575            self.id
12576        }
12577
12578        fn token_type(&self) -> i32 {
12579            self.spec.token_type
12580        }
12581
12582        fn channel(&self) -> i32 {
12583            self.spec.channel
12584        }
12585
12586        fn start(&self) -> usize {
12587            self.spec.start
12588        }
12589
12590        fn stop(&self) -> usize {
12591            self.spec.stop
12592        }
12593
12594        fn line(&self) -> usize {
12595            self.spec.line
12596        }
12597
12598        fn column(&self) -> usize {
12599            self.spec.column
12600        }
12601
12602        fn text(&self) -> Option<&str> {
12603            self.spec.text.as_deref()
12604        }
12605
12606        fn source_name(&self) -> &str {
12607            &self.source_name
12608        }
12609
12610        fn start_byte(&self) -> usize {
12611            self.spec.start_byte
12612        }
12613
12614        fn stop_byte(&self) -> usize {
12615            self.spec.stop_byte
12616        }
12617    }
12618
12619    #[derive(Debug)]
12620    struct Source {
12621        tokens: Vec<TestToken>,
12622        index: usize,
12623    }
12624
12625    impl TokenSource for Source {
12626        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12627            let token = self
12628                .tokens
12629                .get(self.index)
12630                .cloned()
12631                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
12632            self.index += 1;
12633            sink.push(token.spec)
12634        }
12635
12636        fn line(&self) -> usize {
12637            1
12638        }
12639
12640        fn column(&self) -> usize {
12641            self.index
12642        }
12643
12644        fn source_name(&self) -> &'static str {
12645            "parser-test"
12646        }
12647    }
12648
12649    #[derive(Clone, Debug, Eq, PartialEq)]
12650    struct RecordedDiagnostic {
12651        grammar_file_name: String,
12652        line: usize,
12653        column: usize,
12654        message: String,
12655        error: Option<AntlrError>,
12656    }
12657
12658    #[derive(Clone, Debug)]
12659    struct RecordingErrorListener {
12660        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
12661    }
12662
12663    impl<R> crate::ErrorListener<R> for RecordingErrorListener
12664    where
12665        R: Recognizer + ?Sized,
12666    {
12667        fn syntax_error(
12668            &mut self,
12669            recognizer: &R,
12670            line: usize,
12671            column: usize,
12672            message: &str,
12673            error: Option<&AntlrError>,
12674        ) {
12675            self.diagnostics
12676                .lock()
12677                .expect("recorded diagnostics lock")
12678                .push(RecordedDiagnostic {
12679                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
12680                    line,
12681                    column,
12682                    message: message.to_owned(),
12683                    error: error.cloned(),
12684                });
12685        }
12686    }
12687
12688    #[derive(Debug)]
12689    struct ReportingSource {
12690        source: Source,
12691        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
12692    }
12693
12694    impl TokenSource for ReportingSource {
12695        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12696            self.source.next_token(sink)
12697        }
12698
12699        fn line(&self) -> usize {
12700            self.source.line()
12701        }
12702
12703        fn column(&self) -> usize {
12704            self.source.column()
12705        }
12706
12707        fn source_name(&self) -> &str {
12708            self.source.source_name()
12709        }
12710
12711        fn report_error(&self, error: &TokenSourceError) -> bool {
12712            self.diagnostics.borrow_mut().push(error.clone());
12713            true
12714        }
12715    }
12716
12717    fn mini_parser_data() -> RecognizerData {
12718        RecognizerData::new(
12719            "Mini.g4",
12720            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12721        )
12722        .with_rule_names(["s"])
12723    }
12724
12725    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
12726        let data = mini_parser_data();
12727        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
12728    }
12729
12730    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
12731    where
12732        H: SemanticHooks,
12733    {
12734        BaseParser::with_semantic_hooks(
12735            CommonTokenStream::new(Source { tokens, index: 0 }),
12736            mini_parser_data(),
12737            hooks,
12738        )
12739    }
12740
12741    #[test]
12742    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
12743        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
12744        parser.remove_error_listeners();
12745        let diagnostics = Arc::new(Mutex::new(Vec::new()));
12746        parser.add_error_listener(RecordingErrorListener {
12747            diagnostics: Arc::clone(&diagnostics),
12748        });
12749        let parser_diagnostics = [ParserDiagnostic {
12750            line: 1,
12751            column: 2,
12752            message: "missing 'x' at 'y'".to_owned(),
12753        }];
12754        let token_errors = [
12755            TokenSourceError::new(1, 1, "token recognition error at: '@'"),
12756            TokenSourceError::new(1, 3, "token recognition error at: '#'"),
12757        ];
12758
12759        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12760
12761        // The interleaved token/parser diagnostic stream (ordering, columns, messages) is one
12762        // reviewable snapshot instead of three hand-written RecordedDiagnostic literals.
12763        insta::assert_debug_snapshot!(
12764            "parser_dispatches_recovery_diagnostics_through_registered_listeners",
12765            *diagnostics.lock().expect("recorded diagnostics lock")
12766        );
12767
12768        parser.remove_error_listeners();
12769        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12770        assert_eq!(
12771            diagnostics.lock().expect("recorded diagnostics lock").len(),
12772            3
12773        );
12774    }
12775
12776    #[test]
12777    fn parser_leaves_token_errors_to_source_owned_listeners() {
12778        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
12779        let source = ReportingSource {
12780            source: Source {
12781                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
12782                index: 0,
12783            },
12784            diagnostics: Rc::clone(&source_diagnostics),
12785        };
12786        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
12787        parser.remove_error_listeners();
12788        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
12789        parser.add_error_listener(RecordingErrorListener {
12790            diagnostics: Arc::clone(&parser_diagnostics),
12791        });
12792        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
12793
12794        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
12795
12796        assert_eq!(*source_diagnostics.borrow(), [source_error]);
12797        assert!(
12798            parser_diagnostics
12799                .lock()
12800                .expect("recorded diagnostics lock")
12801                .is_empty()
12802        );
12803    }
12804
12805    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
12806        builder.finish().expect("valid packed parser ATN")
12807    }
12808
12809    fn nested_rule_chain_atn(depth: usize) -> Atn {
12810        nested_rule_graph_atn(depth, false, false)
12811    }
12812
12813    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
12814        assert!(depth > 0);
12815        let mut atn = ParserAtnBuilder::new(2);
12816        let mut starts = Vec::with_capacity(depth);
12817        let mut stops = Vec::with_capacity(depth);
12818        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
12819        for rule_index in 0..depth {
12820            starts.push(
12821                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
12822                    .expect("rule start")
12823                    .index(),
12824            );
12825        }
12826        for rule_index in 0..depth {
12827            stops.push(
12828                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
12829                    .expect("rule stop")
12830                    .index(),
12831            );
12832        }
12833        if consuming_follows {
12834            for rule_index in 0..depth - 1 {
12835                follows.push(
12836                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
12837                        .expect("rule follow")
12838                        .index(),
12839                );
12840            }
12841        }
12842        atn.set_rule_to_start_state(starts.clone())
12843            .expect("rule start states");
12844        atn.set_rule_to_stop_state(stops.clone())
12845            .expect("rule stop states");
12846        for rule_index in 0..depth - 1 {
12847            let follow_state = if consuming_follows {
12848                follows[rule_index]
12849            } else {
12850                stops[rule_index]
12851            };
12852            atn.add_transition(
12853                starts[rule_index],
12854                ParserTransitionSpec::Rule {
12855                    target: starts[rule_index + 1],
12856                    rule_index: rule_index + 1,
12857                    follow_state,
12858                    precedence: 0,
12859                },
12860            )
12861            .expect("nested rule transition");
12862            if branching {
12863                atn.add_transition(
12864                    starts[rule_index],
12865                    ParserTransitionSpec::Atom {
12866                        target: stops[rule_index],
12867                        label: 2,
12868                    },
12869                )
12870                .expect("dead branch transition");
12871            }
12872            if consuming_follows {
12873                atn.add_transition(
12874                    follow_state,
12875                    ParserTransitionSpec::Atom {
12876                        target: stops[rule_index],
12877                        label: 1,
12878                    },
12879                )
12880                .expect("consuming follow transition");
12881            }
12882        }
12883        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
12884        atn.add_transition(
12885            starts[depth - 1],
12886            ParserTransitionSpec::Set {
12887                target: stops[depth - 1],
12888                set: token_set,
12889            },
12890        )
12891        .expect("terminal set transition");
12892        if branching {
12893            atn.add_transition(
12894                starts[depth - 1],
12895                ParserTransitionSpec::Atom {
12896                    target: stops[depth - 1],
12897                    label: 2,
12898                },
12899            )
12900            .expect("dead leaf branch transition");
12901        }
12902        finish_atn(atn)
12903    }
12904
12905    fn ordinary_star_loop_atn() -> Atn {
12906        let mut atn = ParserAtnBuilder::new(2);
12907        for (state_number, kind, rule_index) in [
12908            (0, AtnStateKind::RuleStart, 0),
12909            (1, AtnStateKind::StarLoopEntry, 0),
12910            (2, AtnStateKind::Basic, 0),
12911            (3, AtnStateKind::StarLoopBack, 0),
12912            (4, AtnStateKind::LoopEnd, 0),
12913            (5, AtnStateKind::Basic, 0),
12914            (6, AtnStateKind::RuleStop, 0),
12915            (7, AtnStateKind::RuleStart, 1),
12916            (8, AtnStateKind::Basic, 1),
12917            (9, AtnStateKind::RuleStop, 1),
12918        ] {
12919            assert_eq!(
12920                atn.add_state(kind, Some(rule_index))
12921                    .expect("state")
12922                    .index(),
12923                state_number
12924            );
12925        }
12926        atn.set_rule_to_start_state(vec![0, 7])
12927            .expect("rule start states");
12928        atn.set_rule_to_stop_state(vec![6, 9])
12929            .expect("rule stop states");
12930        atn.add_decision_state(1).expect("decision state");
12931        atn.set_loop_back_state(4, 3).expect("loop back state");
12932        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12933            .expect("transition");
12934        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12935            .expect("transition");
12936        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12937            .expect("transition");
12938        atn.add_transition(
12939            2,
12940            ParserTransitionSpec::Rule {
12941                target: 7,
12942                rule_index: 1,
12943                follow_state: 3,
12944                precedence: 0,
12945            },
12946        )
12947        .expect("transition");
12948        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12949            .expect("transition");
12950        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12951            .expect("transition");
12952        atn.add_transition(
12953            5,
12954            ParserTransitionSpec::Atom {
12955                target: 6,
12956                label: TOKEN_EOF,
12957            },
12958        )
12959        .expect("transition");
12960        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12961            .expect("transition");
12962        atn.add_transition(
12963            8,
12964            ParserTransitionSpec::Atom {
12965                target: 9,
12966                label: 1,
12967            },
12968        )
12969        .expect("transition");
12970        finish_atn(atn)
12971    }
12972
12973    /// ATN for `s : (X | X X)* EOF`.
12974    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12975        let mut atn = ParserAtnBuilder::new(1);
12976        for (state_number, kind) in [
12977            (0, AtnStateKind::RuleStart),
12978            (1, AtnStateKind::StarLoopEntry),
12979            (2, AtnStateKind::StarBlockStart),
12980            (3, AtnStateKind::Basic),
12981            (4, AtnStateKind::BlockEnd),
12982            (5, AtnStateKind::StarLoopBack),
12983            (6, AtnStateKind::LoopEnd),
12984            (7, AtnStateKind::Basic),
12985            (8, AtnStateKind::RuleStop),
12986        ] {
12987            assert_eq!(
12988                atn.add_state(kind, Some(0)).expect("state").index(),
12989                state_number
12990            );
12991        }
12992        atn.set_rule_to_start_state(vec![0])
12993            .expect("rule start states");
12994        atn.set_rule_to_stop_state(vec![8])
12995            .expect("rule stop states");
12996        atn.set_end_state(2, 4).expect("block end state");
12997        atn.set_loop_back_state(6, 5).expect("loop back state");
12998        atn.add_decision_state(1).expect("decision state");
12999        atn.add_decision_state(2).expect("decision state");
13000        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13001            .expect("transition");
13002        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13003            .expect("transition");
13004        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
13005            .expect("transition");
13006        atn.add_transition(
13007            2,
13008            ParserTransitionSpec::Atom {
13009                target: 4,
13010                label: 1,
13011            },
13012        )
13013        .expect("transition");
13014        atn.add_transition(
13015            2,
13016            ParserTransitionSpec::Atom {
13017                target: 3,
13018                label: 1,
13019            },
13020        )
13021        .expect("transition");
13022        atn.add_transition(
13023            3,
13024            ParserTransitionSpec::Atom {
13025                target: 4,
13026                label: 1,
13027            },
13028        )
13029        .expect("transition");
13030        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13031            .expect("transition");
13032        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
13033            .expect("transition");
13034        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13035            .expect("transition");
13036        atn.add_transition(
13037            7,
13038            ParserTransitionSpec::Atom {
13039                target: 8,
13040                label: TOKEN_EOF,
13041            },
13042        )
13043        .expect("transition");
13044        finish_atn(atn)
13045    }
13046
13047    fn ordinary_plus_loop_atn() -> Atn {
13048        let mut atn = ParserAtnBuilder::new(2);
13049        for (state_number, kind, rule_index) in [
13050            (0, AtnStateKind::RuleStart, 0),
13051            (1, AtnStateKind::Basic, 0),
13052            (2, AtnStateKind::PlusLoopBack, 0),
13053            (3, AtnStateKind::LoopEnd, 0),
13054            (4, AtnStateKind::Basic, 0),
13055            (5, AtnStateKind::RuleStop, 0),
13056            (6, AtnStateKind::RuleStart, 1),
13057            (7, AtnStateKind::Basic, 1),
13058            (8, AtnStateKind::RuleStop, 1),
13059        ] {
13060            assert_eq!(
13061                atn.add_state(kind, Some(rule_index))
13062                    .expect("state")
13063                    .index(),
13064                state_number
13065            );
13066        }
13067        atn.set_rule_to_start_state(vec![0, 6])
13068            .expect("rule start states");
13069        atn.set_rule_to_stop_state(vec![5, 8])
13070            .expect("rule stop states");
13071        atn.add_decision_state(2).expect("decision state");
13072        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13073            .expect("transition");
13074        atn.add_transition(
13075            1,
13076            ParserTransitionSpec::Rule {
13077                target: 6,
13078                rule_index: 1,
13079                follow_state: 2,
13080                precedence: 0,
13081            },
13082        )
13083        .expect("transition");
13084        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
13085            .expect("transition");
13086        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13087            .expect("transition");
13088        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13089            .expect("transition");
13090        atn.add_transition(
13091            4,
13092            ParserTransitionSpec::Atom {
13093                target: 5,
13094                label: TOKEN_EOF,
13095            },
13096        )
13097        .expect("transition");
13098        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13099            .expect("transition");
13100        atn.add_transition(
13101            7,
13102            ParserTransitionSpec::Atom {
13103                target: 8,
13104                label: 1,
13105            },
13106        )
13107        .expect("transition");
13108        finish_atn(atn)
13109    }
13110
13111    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
13112        let mut tokens = (0..count)
13113            .map(|_| TestToken::new(1).with_text("x"))
13114            .collect::<Vec<_>>();
13115        tokens.push(TestToken::eof("parser-test", count, 1, count));
13116        tokens
13117    }
13118
13119    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
13120        let mut atn = ParserAtnBuilder::new(2);
13121        assert_eq!(
13122            atn.add_state(AtnStateKind::RuleStart, Some(0))
13123                .expect("state")
13124                .index(),
13125            0
13126        );
13127        assert_eq!(
13128            atn.add_state(AtnStateKind::Basic, Some(0))
13129                .expect("state")
13130                .index(),
13131            1
13132        );
13133        assert_eq!(
13134            atn.add_state(AtnStateKind::Basic, Some(0))
13135                .expect("state")
13136                .index(),
13137            2
13138        );
13139        assert_eq!(
13140            atn.add_state(AtnStateKind::RuleStart, Some(1))
13141                .expect("state")
13142                .index(),
13143            3
13144        );
13145        atn.set_left_recursive_rule(3)
13146            .expect("left-recursive rule start");
13147        assert_eq!(
13148            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
13149                .expect("state")
13150                .index(),
13151            4
13152        );
13153        atn.set_precedence_rule_decision(4)
13154            .expect("precedence decision");
13155        assert_eq!(
13156            atn.add_state(AtnStateKind::Basic, Some(1))
13157                .expect("state")
13158                .index(),
13159            5
13160        );
13161        assert_eq!(
13162            atn.add_state(AtnStateKind::Basic, Some(1))
13163                .expect("state")
13164                .index(),
13165            6
13166        );
13167        assert_eq!(
13168            atn.add_state(AtnStateKind::LoopEnd, Some(1))
13169                .expect("state")
13170                .index(),
13171            7
13172        );
13173        assert_eq!(
13174            atn.add_state(AtnStateKind::RuleStop, Some(1))
13175                .expect("state")
13176                .index(),
13177            8
13178        );
13179        assert_eq!(
13180            atn.add_state(AtnStateKind::RuleStop, Some(0))
13181                .expect("state")
13182                .index(),
13183            9
13184        );
13185        atn.set_rule_to_start_state(vec![0, 3])
13186            .expect("rule start states");
13187        atn.set_rule_to_stop_state(vec![9, 8])
13188            .expect("rule stop states");
13189        atn.add_transition(
13190            1,
13191            ParserTransitionSpec::Rule {
13192                target: 3,
13193                rule_index: 1,
13194                follow_state: 2,
13195                precedence: 0,
13196            },
13197        )
13198        .expect("transition");
13199        atn.add_transition(
13200            2,
13201            ParserTransitionSpec::Atom {
13202                target: 9,
13203                label: caller_symbol,
13204            },
13205        )
13206        .expect("transition");
13207        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13208            .expect("transition");
13209        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
13210            .expect("transition");
13211        atn.add_transition(
13212            5,
13213            ParserTransitionSpec::Precedence {
13214                target: 6,
13215                precedence: 1,
13216            },
13217        )
13218        .expect("transition");
13219        atn.add_transition(
13220            6,
13221            ParserTransitionSpec::Atom {
13222                target: 4,
13223                label: 1,
13224            },
13225        )
13226        .expect("transition");
13227        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13228            .expect("transition");
13229        finish_atn(atn)
13230    }
13231
13232    fn labeled_left_recursive_operator_atn() -> Atn {
13233        let mut atn = ParserAtnBuilder::new(4);
13234        for (state, kind) in [
13235            (0, AtnStateKind::RuleStart),
13236            (1, AtnStateKind::BlockStart),
13237            (2, AtnStateKind::StarLoopEntry),
13238            (3, AtnStateKind::StarBlockStart),
13239            (4, AtnStateKind::Basic),
13240            (5, AtnStateKind::Basic),
13241            (6, AtnStateKind::Basic),
13242            (7, AtnStateKind::StarLoopBack),
13243            (8, AtnStateKind::LoopEnd),
13244            (9, AtnStateKind::RuleStop),
13245        ] {
13246            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13247        }
13248        atn.set_left_recursive_rule(0)
13249            .expect("left-recursive rule start");
13250        atn.set_precedence_rule_decision(2)
13251            .expect("precedence decision");
13252        atn.set_loop_back_state(8, 7).expect("loop-back state");
13253        atn.set_rule_to_start_state(vec![0])
13254            .expect("rule start states");
13255        atn.set_rule_to_stop_state(vec![9])
13256            .expect("rule stop states");
13257        for state in [1, 2, 3] {
13258            atn.add_decision_state(state).expect("decision state");
13259        }
13260        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
13261            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
13262                .expect("epsilon transition");
13263        }
13264        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
13265            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
13266                .expect("token transition");
13267        }
13268        for (target, precedence) in [(4, 2), (5, 1)] {
13269            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
13270                .expect("operator precedence");
13271        }
13272        finish_atn(atn)
13273    }
13274
13275    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
13276        let mut parser = mini_parser(vec![
13277            TestToken::new(symbol).with_text("lookahead"),
13278            TestToken::eof("parser-test", 1, 1, 1),
13279        ]);
13280        parser.rule_context_stack = vec![
13281            RuleContextFrame {
13282                rule_index: 0,
13283                invoking_state: -1,
13284            },
13285            RuleContextFrame {
13286                rule_index: 1,
13287                invoking_state: 1,
13288            },
13289        ];
13290        parser
13291    }
13292
13293    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
13294        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
13295        //   prec 2: token 1, token 1  (shift `>>`)
13296        //   prec 1: token 1           (relational `>`)
13297        let mut atn = ParserAtnBuilder::new(1);
13298        for (state, kind, rule) in [
13299            (0, AtnStateKind::RuleStart, 0),
13300            (1, AtnStateKind::StarLoopEntry, 0),
13301            (2, AtnStateKind::Basic, 0), // ops hub
13302            (3, AtnStateKind::Basic, 0), // shift prec
13303            (4, AtnStateKind::Basic, 0), // shift first >
13304            (5, AtnStateKind::Basic, 0), // shift second >
13305            (6, AtnStateKind::Basic, 0), // rel prec
13306            (7, AtnStateKind::Basic, 0), // rel >
13307            (8, AtnStateKind::LoopEnd, 0),
13308            (9, AtnStateKind::RuleStop, 0),
13309        ] {
13310            assert_eq!(
13311                atn.add_state(kind, Some(rule)).expect("state").index(),
13312                state
13313            );
13314            if state == 0 {
13315                atn.set_left_recursive_rule(state)
13316                    .expect("left-recursive rule start");
13317            } else if state == 1 {
13318                atn.set_precedence_rule_decision(state)
13319                    .expect("precedence decision");
13320            }
13321        }
13322        atn.set_rule_to_start_state(vec![0])
13323            .expect("rule start states");
13324        atn.set_rule_to_stop_state(vec![9])
13325            .expect("rule stop states");
13326        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13327            .expect("ops");
13328        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13329            .expect("exit");
13330        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13331            .expect("to shift");
13332        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13333            .expect("to rel");
13334        atn.add_transition(
13335            3,
13336            ParserTransitionSpec::Precedence {
13337                target: 4,
13338                precedence: 2,
13339            },
13340        )
13341        .expect("shift prec");
13342        atn.add_transition(
13343            4,
13344            ParserTransitionSpec::Atom {
13345                target: 5,
13346                label: 1,
13347            },
13348        )
13349        .expect("shift first >");
13350        atn.add_transition(
13351            5,
13352            ParserTransitionSpec::Atom {
13353                target: 1,
13354                label: 1,
13355            },
13356        )
13357        .expect("shift second >");
13358        atn.add_transition(
13359            6,
13360            ParserTransitionSpec::Precedence {
13361                target: 7,
13362                precedence: 1,
13363            },
13364        )
13365        .expect("rel prec");
13366        atn.add_transition(
13367            7,
13368            ParserTransitionSpec::Atom {
13369                target: 1,
13370                label: 1,
13371            },
13372        )
13373        .expect("rel >");
13374        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13375            .expect("loop end");
13376        finish_atn(atn)
13377    }
13378
13379    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
13380        let mut atn = ParserAtnBuilder::new(2);
13381        for (state, kind, rule) in [
13382            (0, AtnStateKind::RuleStart, 0),
13383            (1, AtnStateKind::StarLoopEntry, 0),
13384            (2, AtnStateKind::Basic, 0),
13385            (3, AtnStateKind::Basic, 0),
13386            (4, AtnStateKind::Basic, 0),
13387            (5, AtnStateKind::Basic, 0),
13388            (6, AtnStateKind::Basic, 0),
13389            (7, AtnStateKind::Basic, 0),
13390            (8, AtnStateKind::LoopEnd, 0),
13391            (9, AtnStateKind::RuleStop, 0),
13392            (10, AtnStateKind::RuleStart, 1),
13393            (11, AtnStateKind::Basic, 1),
13394            (12, AtnStateKind::RuleStop, 1),
13395        ] {
13396            assert_eq!(
13397                atn.add_state(kind, Some(rule)).expect("state").index(),
13398                state
13399            );
13400            if state == 0 {
13401                atn.set_left_recursive_rule(state)
13402                    .expect("left-recursive rule start");
13403            } else if state == 1 {
13404                atn.set_precedence_rule_decision(state)
13405                    .expect("precedence decision");
13406            }
13407        }
13408        atn.set_rule_to_start_state(vec![0, 10])
13409            .expect("rule start states");
13410        atn.set_rule_to_stop_state(vec![9, 12])
13411            .expect("rule stop states");
13412        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13413            .expect("ops");
13414        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13415            .expect("exit");
13416        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13417            .expect("to shift");
13418        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13419            .expect("to relational");
13420        atn.add_transition(
13421            3,
13422            ParserTransitionSpec::Precedence {
13423                target: 4,
13424                precedence: 2,
13425            },
13426        )
13427        .expect("shift precedence");
13428        atn.add_transition(
13429            4,
13430            ParserTransitionSpec::Rule {
13431                target: 10,
13432                rule_index: 1,
13433                follow_state: 5,
13434                precedence: 0,
13435            },
13436        )
13437        .expect("first shift token helper");
13438        atn.add_transition(
13439            5,
13440            ParserTransitionSpec::Atom {
13441                target: 1,
13442                label: 1,
13443            },
13444        )
13445        .expect("second shift token");
13446        atn.add_transition(
13447            6,
13448            ParserTransitionSpec::Precedence {
13449                target: 7,
13450                precedence: 1,
13451            },
13452        )
13453        .expect("relational precedence");
13454        atn.add_transition(
13455            7,
13456            ParserTransitionSpec::Atom {
13457                target: 1,
13458                label: 1,
13459            },
13460        )
13461        .expect("relational token");
13462        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13463            .expect("loop end");
13464        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13465            .expect("helper entry");
13466        atn.add_transition(
13467            11,
13468            ParserTransitionSpec::Atom {
13469                target: 12,
13470                label: 1,
13471            },
13472        )
13473        .expect("first shift token");
13474        finish_atn(atn)
13475    }
13476
13477    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
13478        let mut atn = ParserAtnBuilder::new(1);
13479        for (state, kind) in [
13480            (0, AtnStateKind::RuleStart),
13481            (1, AtnStateKind::StarLoopEntry),
13482            (2, AtnStateKind::Basic),
13483            (3, AtnStateKind::Basic),
13484            (4, AtnStateKind::Basic),
13485            (5, AtnStateKind::Basic),
13486            (6, AtnStateKind::Basic),
13487            (7, AtnStateKind::Basic),
13488            (8, AtnStateKind::Basic),
13489            (9, AtnStateKind::LoopEnd),
13490            (10, AtnStateKind::RuleStop),
13491        ] {
13492            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13493            if state == 0 {
13494                atn.set_left_recursive_rule(state)
13495                    .expect("left-recursive rule start");
13496            } else if state == 1 {
13497                atn.set_precedence_rule_decision(state)
13498                    .expect("precedence decision");
13499            }
13500        }
13501        atn.set_rule_to_start_state(vec![0])
13502            .expect("rule start states");
13503        atn.set_rule_to_stop_state(vec![10])
13504            .expect("rule stop states");
13505        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13506            .expect("ops");
13507        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
13508            .expect("exit");
13509        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13510            .expect("to multi-token operator");
13511        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13512            .expect("to predicate operator");
13513        atn.add_transition(
13514            3,
13515            ParserTransitionSpec::Precedence {
13516                target: 4,
13517                precedence: 2,
13518            },
13519        )
13520        .expect("multi-token precedence");
13521        atn.add_transition(
13522            4,
13523            ParserTransitionSpec::Atom {
13524                target: 5,
13525                label: 1,
13526            },
13527        )
13528        .expect("multi-token first");
13529        atn.add_transition(
13530            5,
13531            ParserTransitionSpec::Atom {
13532                target: 1,
13533                label: 1,
13534            },
13535        )
13536        .expect("multi-token second");
13537        atn.add_transition(
13538            6,
13539            ParserTransitionSpec::Precedence {
13540                target: 7,
13541                precedence: 2,
13542            },
13543        )
13544        .expect("predicate precedence");
13545        atn.add_transition(
13546            7,
13547            ParserTransitionSpec::Predicate {
13548                target: 8,
13549                rule_index: 0,
13550                pred_index: 0,
13551                context_dependent: false,
13552            },
13553        )
13554        .expect("operator predicate");
13555        atn.add_transition(
13556            8,
13557            ParserTransitionSpec::Atom {
13558                target: 1,
13559                label: 1,
13560            },
13561        )
13562        .expect("predicate single token");
13563        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13564            .expect("loop end");
13565        finish_atn(atn)
13566    }
13567
13568    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
13569        let mut atn = ParserAtnBuilder::new(2);
13570        for (state, kind, rule) in [
13571            (0, AtnStateKind::RuleStart, 0),
13572            (1, AtnStateKind::StarLoopEntry, 0),
13573            (2, AtnStateKind::Basic, 0),
13574            (3, AtnStateKind::Basic, 0),
13575            (4, AtnStateKind::Basic, 0),
13576            (5, AtnStateKind::LoopEnd, 0),
13577            (6, AtnStateKind::RuleStop, 0),
13578            (7, AtnStateKind::RuleStart, 1),
13579            (8, AtnStateKind::RuleStop, 1),
13580            (9, AtnStateKind::Basic, 1),
13581        ] {
13582            assert_eq!(
13583                atn.add_state(kind, Some(rule)).expect("state").index(),
13584                state
13585            );
13586            if state == 0 {
13587                atn.set_left_recursive_rule(state)
13588                    .expect("left-recursive rule start");
13589            } else if state == 1 {
13590                atn.set_precedence_rule_decision(state)
13591                    .expect("precedence decision");
13592            }
13593        }
13594        atn.set_rule_to_start_state(vec![0, 7])
13595            .expect("rule start states");
13596        atn.set_rule_to_stop_state(vec![6, 8])
13597            .expect("rule stop states");
13598        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13599            .expect("transition");
13600        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13601            .expect("transition");
13602        atn.add_transition(
13603            2,
13604            ParserTransitionSpec::Precedence {
13605                target: 3,
13606                precedence: 3,
13607            },
13608        )
13609        .expect("transition");
13610        atn.add_transition(
13611            3,
13612            ParserTransitionSpec::Rule {
13613                target: 7,
13614                rule_index: 1,
13615                follow_state: 4,
13616                precedence: 0,
13617            },
13618        )
13619        .expect("transition");
13620        atn.add_transition(
13621            4,
13622            ParserTransitionSpec::Atom {
13623                target: 1,
13624                label: 1,
13625            },
13626        )
13627        .expect("transition");
13628        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13629            .expect("transition");
13630        atn.add_transition(
13631            7,
13632            ParserTransitionSpec::Precedence {
13633                target: 9,
13634                precedence: 1,
13635            },
13636        )
13637        .expect("transition");
13638        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
13639            .expect("transition");
13640        finish_atn(atn)
13641    }
13642
13643    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
13644        let mut atn = ParserAtnBuilder::new(2);
13645        for (state, kind) in [
13646            (0, AtnStateKind::RuleStart),
13647            (1, AtnStateKind::StarLoopEntry),
13648            (2, AtnStateKind::Basic),
13649            (3, AtnStateKind::Basic),
13650            (4, AtnStateKind::Basic),
13651            (5, AtnStateKind::LoopEnd),
13652            (6, AtnStateKind::RuleStop),
13653        ] {
13654            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13655            if state == 0 {
13656                atn.set_left_recursive_rule(state)
13657                    .expect("left-recursive rule start");
13658            } else if state == 1 {
13659                atn.set_precedence_rule_decision(state)
13660                    .expect("precedence decision");
13661            }
13662        }
13663        atn.set_rule_to_start_state(vec![0])
13664            .expect("rule start states");
13665        atn.set_rule_to_stop_state(vec![6])
13666            .expect("rule stop states");
13667        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13668            .expect("transition");
13669        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13670            .expect("transition");
13671        atn.add_transition(
13672            2,
13673            ParserTransitionSpec::Precedence {
13674                target: 3,
13675                precedence: 1,
13676            },
13677        )
13678        .expect("transition");
13679        atn.add_transition(
13680            3,
13681            ParserTransitionSpec::Predicate {
13682                target: 4,
13683                rule_index: 0,
13684                pred_index: 0,
13685                context_dependent: false,
13686            },
13687        )
13688        .expect("transition");
13689        atn.add_transition(
13690            4,
13691            ParserTransitionSpec::Atom {
13692                target: 1,
13693                label: 1,
13694            },
13695        )
13696        .expect("transition");
13697        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13698            .expect("transition");
13699        finish_atn(atn)
13700    }
13701
13702    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
13703        let mut atn = ParserAtnBuilder::new(2);
13704        for (state, kind, rule) in [
13705            (0, AtnStateKind::RuleStart, 0),
13706            (1, AtnStateKind::Basic, 0),
13707            (2, AtnStateKind::Basic, 0),
13708            (3, AtnStateKind::Basic, 0),
13709            (4, AtnStateKind::RuleStop, 0),
13710            (5, AtnStateKind::RuleStart, 1),
13711            (6, AtnStateKind::StarLoopEntry, 1),
13712            (7, AtnStateKind::Basic, 1),
13713            (8, AtnStateKind::Basic, 1),
13714            (9, AtnStateKind::LoopEnd, 1),
13715            (10, AtnStateKind::RuleStop, 1),
13716            (11, AtnStateKind::RuleStart, 2),
13717            (12, AtnStateKind::RuleStop, 2),
13718        ] {
13719            assert_eq!(
13720                atn.add_state(kind, Some(rule)).expect("state").index(),
13721                state
13722            );
13723            if state == 5 {
13724                atn.set_left_recursive_rule(state)
13725                    .expect("left-recursive rule start");
13726            } else if state == 6 {
13727                atn.set_precedence_rule_decision(state)
13728                    .expect("precedence decision");
13729            }
13730        }
13731        atn.set_rule_to_start_state(vec![0, 5, 11])
13732            .expect("rule start states");
13733        atn.set_rule_to_stop_state(vec![4, 10, 12])
13734            .expect("rule stop states");
13735        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13736            .expect("transition");
13737        atn.add_transition(
13738            1,
13739            ParserTransitionSpec::Rule {
13740                target: 5,
13741                rule_index: 1,
13742                follow_state: 2,
13743                precedence: 0,
13744            },
13745        )
13746        .expect("transition");
13747        atn.add_transition(
13748            2,
13749            ParserTransitionSpec::Rule {
13750                target: 11,
13751                rule_index: 2,
13752                follow_state: 3,
13753                precedence: 0,
13754            },
13755        )
13756        .expect("transition");
13757        atn.add_transition(
13758            3,
13759            ParserTransitionSpec::Atom {
13760                target: 4,
13761                label: caller_symbol,
13762            },
13763        )
13764        .expect("transition");
13765        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13766            .expect("transition");
13767        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
13768            .expect("transition");
13769        atn.add_transition(
13770            7,
13771            ParserTransitionSpec::Precedence {
13772                target: 8,
13773                precedence: 1,
13774            },
13775        )
13776        .expect("transition");
13777        atn.add_transition(
13778            8,
13779            ParserTransitionSpec::Atom {
13780                target: 6,
13781                label: 1,
13782            },
13783        )
13784        .expect("transition");
13785        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13786            .expect("transition");
13787        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
13788            .expect("transition");
13789        finish_atn(atn)
13790    }
13791
13792    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
13793        let mut atn = ParserAtnBuilder::new(2);
13794        for (state, kind, rule) in [
13795            (0, AtnStateKind::RuleStart, 0),
13796            (1, AtnStateKind::Basic, 0),
13797            (2, AtnStateKind::Basic, 0),
13798            (3, AtnStateKind::RuleStop, 0),
13799            (4, AtnStateKind::RuleStart, 1),
13800            (5, AtnStateKind::Basic, 1),
13801            (6, AtnStateKind::Basic, 1),
13802            (7, AtnStateKind::RuleStop, 1),
13803            (8, AtnStateKind::RuleStart, 2),
13804            (9, AtnStateKind::StarLoopEntry, 2),
13805            (10, AtnStateKind::Basic, 2),
13806            (11, AtnStateKind::Basic, 2),
13807            (12, AtnStateKind::LoopEnd, 2),
13808            (13, AtnStateKind::RuleStop, 2),
13809        ] {
13810            assert_eq!(
13811                atn.add_state(kind, Some(rule)).expect("state").index(),
13812                state
13813            );
13814            if state == 8 {
13815                atn.set_left_recursive_rule(state)
13816                    .expect("left-recursive rule start");
13817            } else if state == 9 {
13818                atn.set_precedence_rule_decision(state)
13819                    .expect("precedence decision");
13820            }
13821        }
13822        atn.set_rule_to_start_state(vec![0, 4, 8])
13823            .expect("rule start states");
13824        atn.set_rule_to_stop_state(vec![3, 7, 13])
13825            .expect("rule stop states");
13826        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13827            .expect("transition");
13828        atn.add_transition(
13829            1,
13830            ParserTransitionSpec::Rule {
13831                target: 4,
13832                rule_index: 1,
13833                follow_state: 2,
13834                precedence: 0,
13835            },
13836        )
13837        .expect("transition");
13838        atn.add_transition(
13839            2,
13840            ParserTransitionSpec::Atom {
13841                target: 3,
13842                label: caller_symbol,
13843            },
13844        )
13845        .expect("transition");
13846        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13847            .expect("transition");
13848        atn.add_transition(
13849            5,
13850            ParserTransitionSpec::Rule {
13851                target: 8,
13852                rule_index: 2,
13853                follow_state: 6,
13854                precedence: 0,
13855            },
13856        )
13857        .expect("transition");
13858        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13859            .expect("transition");
13860        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13861            .expect("transition");
13862        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
13863            .expect("transition");
13864        atn.add_transition(
13865            10,
13866            ParserTransitionSpec::Precedence {
13867                target: 11,
13868                precedence: 1,
13869            },
13870        )
13871        .expect("transition");
13872        atn.add_transition(
13873            11,
13874            ParserTransitionSpec::Atom {
13875                target: 9,
13876                label: 1,
13877            },
13878        )
13879        .expect("transition");
13880        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
13881            .expect("transition");
13882        finish_atn(atn)
13883    }
13884
13885    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
13886        let mut atn = ParserAtnBuilder::new(2);
13887        for (state, kind, rule) in [
13888            (0, AtnStateKind::RuleStart, 0),
13889            (1, AtnStateKind::Basic, 0),
13890            (2, AtnStateKind::Basic, 0),
13891            (3, AtnStateKind::RuleStop, 0),
13892            (4, AtnStateKind::RuleStart, 1),
13893            (5, AtnStateKind::StarLoopEntry, 1),
13894            (6, AtnStateKind::Basic, 1),
13895            (7, AtnStateKind::Basic, 1),
13896            (8, AtnStateKind::Basic, 1),
13897            (9, AtnStateKind::Basic, 1),
13898            (10, AtnStateKind::LoopEnd, 1),
13899            (11, AtnStateKind::RuleStop, 1),
13900        ] {
13901            assert_eq!(
13902                atn.add_state(kind, Some(rule)).expect("state").index(),
13903                state
13904            );
13905            if state == 4 {
13906                atn.set_left_recursive_rule(state)
13907                    .expect("left-recursive rule start");
13908            } else if state == 5 {
13909                atn.set_precedence_rule_decision(state)
13910                    .expect("precedence decision");
13911            }
13912        }
13913        atn.set_rule_to_start_state(vec![0, 4])
13914            .expect("rule start states");
13915        atn.set_rule_to_stop_state(vec![3, 11])
13916            .expect("rule stop states");
13917        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13918            .expect("transition");
13919        atn.add_transition(
13920            1,
13921            ParserTransitionSpec::Rule {
13922                target: 4,
13923                rule_index: 1,
13924                follow_state: 2,
13925                precedence: 0,
13926            },
13927        )
13928        .expect("transition");
13929        atn.add_transition(
13930            2,
13931            ParserTransitionSpec::Atom {
13932                target: 3,
13933                label: caller_symbol,
13934            },
13935        )
13936        .expect("transition");
13937        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13938            .expect("transition");
13939        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
13940            .expect("transition");
13941        atn.add_transition(
13942            6,
13943            ParserTransitionSpec::Precedence {
13944                target: 7,
13945                precedence: 1,
13946            },
13947        )
13948        .expect("transition");
13949        atn.add_transition(
13950            7,
13951            ParserTransitionSpec::Atom {
13952                target: 8,
13953                label: 1,
13954            },
13955        )
13956        .expect("transition");
13957        atn.add_transition(
13958            8,
13959            ParserTransitionSpec::Rule {
13960                target: 4,
13961                rule_index: 1,
13962                follow_state: 9,
13963                precedence: 2,
13964            },
13965        )
13966        .expect("transition");
13967        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13968            .expect("transition");
13969        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13970            .expect("transition");
13971        finish_atn(atn)
13972    }
13973
13974    #[test]
13975    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13976        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13977        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13978
13979        let mut overlapping = parser_inside_left_recursive_callee(1);
13980        assert_eq!(
13981            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13982            None
13983        );
13984
13985        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13986        assert_eq!(
13987            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13988            Some(true)
13989        );
13990
13991        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13992        assert_eq!(
13993            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13994            Some(false)
13995        );
13996
13997        assert_eq!(
13998            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13999            Some(true),
14000            "overlap results must not leak across ATNs"
14001        );
14002    }
14003
14004    #[test]
14005    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
14006        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
14007        let mut parser = mini_parser(vec![
14008            TestToken::new(1).with_text("operator"),
14009            TestToken::eof("parser-test", 1, 1, 1),
14010        ]);
14011        parser.rule_context_stack = vec![RuleContextFrame {
14012            rule_index: 0,
14013            invoking_state: -1,
14014        }];
14015
14016        assert_eq!(
14017            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14018            Some(true)
14019        );
14020        assert_eq!(
14021            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14022            Some(true),
14023            "cached operator lookahead must preserve the nullable prefix return path"
14024        );
14025        assert_eq!(
14026            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14027            Some(true),
14028            "the nullable child must use its rule-call precedence, not the caller precedence"
14029        );
14030    }
14031
14032    #[test]
14033    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
14034        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
14035        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
14036        // must defer so StarLoopEntry adaptive predict can exit when the second
14037        // `>` is absent (as in `a < b > c`).
14038        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
14039        let mut parser = mini_parser(vec![
14040            TestToken::new(1).with_text(">"),
14041            TestToken::new(2).with_text("id"),
14042            TestToken::eof("parser-test", 1, 1, 1),
14043        ]);
14044        parser.rule_context_stack = vec![RuleContextFrame {
14045            rule_index: 0,
14046            invoking_state: -1,
14047        }];
14048
14049        assert_eq!(
14050            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14051            Some(true),
14052            "at low precedence relational `>` is a single-token operator"
14053        );
14054        assert_eq!(
14055            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
14056            Some(true),
14057            "relational remains single-token at its own precedence"
14058        );
14059        assert_eq!(
14060            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14061            None,
14062            "at shift precedence, bare `>` must not force enter"
14063        );
14064    }
14065
14066    #[test]
14067    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
14068        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
14069        let mut parser = mini_parser(vec![
14070            TestToken::new(1).with_text(">"),
14071            TestToken::new(2).with_text("id"),
14072            TestToken::eof("parser-test", 1, 1, 1),
14073        ]);
14074        parser.rule_context_stack = vec![RuleContextFrame {
14075            rule_index: 0,
14076            invoking_state: -1,
14077        }];
14078
14079        assert_eq!(
14080            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14081            Some(true),
14082            "the direct relational alternative remains a one-token operator"
14083        );
14084        assert_eq!(
14085            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14086            None,
14087            "a token matched in the helper rule must return to the second shift token"
14088        );
14089    }
14090
14091    #[test]
14092    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
14093        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
14094        let mut parser = mini_parser(vec![
14095            TestToken::new(1).with_text(">"),
14096            TestToken::new(2).with_text("id"),
14097            TestToken::eof("parser-test", 1, 1, 1),
14098        ]);
14099        parser.rule_context_stack = vec![RuleContextFrame {
14100            rule_index: 0,
14101            invoking_state: -1,
14102        }];
14103
14104        assert_eq!(
14105            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14106            None,
14107            "a predicate-gated single-token path must not be hidden by a multi-token path"
14108        );
14109    }
14110
14111    #[test]
14112    fn left_recursive_loop_defers_predicate_guarded_operator() {
14113        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
14114        let mut parser = mini_parser_with_hooks(
14115            vec![
14116                TestToken::new(1).with_text("operator"),
14117                TestToken::eof("parser-test", 1, 1, 1),
14118            ],
14119            RejectingPredicateHooks::default(),
14120        );
14121        parser.rule_context_stack = vec![RuleContextFrame {
14122            rule_index: 0,
14123            invoking_state: -1,
14124        }];
14125
14126        assert_eq!(
14127            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14128            None,
14129            "a false predicate must be evaluated before entering the operator alternative"
14130        );
14131        assert_eq!(
14132            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14133            None,
14134            "cached predicate-dependent lookahead must keep deferring"
14135        );
14136    }
14137
14138    #[test]
14139    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
14140        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
14141        let mut parser = parser_inside_left_recursive_callee(1);
14142
14143        assert_eq!(
14144            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14145            None
14146        );
14147        assert_eq!(
14148            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14149            None,
14150            "the cached overlap must preserve the nullable child return path"
14151        );
14152    }
14153
14154    #[test]
14155    fn left_recursive_loop_defers_through_nullable_parent_return() {
14156        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
14157        let mut parser = mini_parser(vec![
14158            TestToken::new(1).with_text("lookahead"),
14159            TestToken::eof("parser-test", 1, 1, 1),
14160        ]);
14161        parser.rule_context_stack = vec![
14162            RuleContextFrame {
14163                rule_index: 0,
14164                invoking_state: -1,
14165            },
14166            RuleContextFrame {
14167                rule_index: 1,
14168                invoking_state: 1,
14169            },
14170            RuleContextFrame {
14171                rule_index: 2,
14172                invoking_state: 5,
14173            },
14174        ];
14175
14176        assert_eq!(
14177            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14178            None,
14179            "a nullable caller must unwind to its parent's consuming follow path"
14180        );
14181        assert_eq!(
14182            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14183            None,
14184            "the caller-overlap cache must not retain a false negative"
14185        );
14186    }
14187
14188    #[test]
14189    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
14190        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
14191        let mut parser = mini_parser(vec![
14192            TestToken::new(1).with_text("lookahead"),
14193            TestToken::eof("parser-test", 1, 1, 1),
14194        ]);
14195        parser.rule_context_stack = vec![
14196            RuleContextFrame {
14197                rule_index: 0,
14198                invoking_state: -1,
14199            },
14200            RuleContextFrame {
14201                rule_index: 1,
14202                invoking_state: 1,
14203            },
14204            RuleContextFrame {
14205                rule_index: 1,
14206                invoking_state: 8,
14207            },
14208        ];
14209
14210        assert_eq!(
14211            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14212            None,
14213            "a recursive operand return must preserve its parent caller context"
14214        );
14215        assert_eq!(
14216            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14217            None,
14218            "the caller-overlap cache must preserve the loop-boundary return"
14219        );
14220    }
14221
14222    fn token_then_eof_atn() -> Atn {
14223        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14224            4, 1, 2, // version, parser, max token type
14225            3, // states
14226            2, 0, // rule start
14227            1, 0, // basic
14228            7, 0, // rule stop
14229            0, // non-greedy states
14230            0, // precedence states
14231            1, // rules
14232            0, // rule 0 start
14233            0, // modes
14234            0, // sets
14235            2, // transitions
14236            0, 1, 5, 1, 0, 0, // match token 1
14237            1, 2, 5, -1, 0, 0, // match EOF
14238            0, // decisions
14239        ]))
14240        .deserialize_parser()
14241        .expect("artificial parser ATN should deserialize")
14242    }
14243
14244    fn epsilon_cycle_atn() -> Atn {
14245        let mut atn = ParserAtnBuilder::new(1);
14246        for (state_number, kind) in [
14247            (0, AtnStateKind::RuleStart),
14248            (1, AtnStateKind::Basic),
14249            (2, AtnStateKind::RuleStop),
14250        ] {
14251            assert_eq!(
14252                atn.add_state(kind, Some(0)).expect("state").index(),
14253                state_number
14254            );
14255        }
14256        atn.set_rule_to_start_state(vec![0])
14257            .expect("rule start states");
14258        atn.set_rule_to_stop_state(vec![2])
14259            .expect("rule stop states");
14260        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14261            .expect("transition");
14262        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
14263            .expect("self-cycle transition");
14264        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14265            .expect("exit transition");
14266        finish_atn(atn)
14267    }
14268
14269    fn eof_then_action_atn() -> Atn {
14270        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14271            4, 1, 1, // version, parser, max token type
14272            3, // states
14273            2, 0, // rule start
14274            1, 0, // basic
14275            7, 0, // rule stop
14276            0, // non-greedy states
14277            0, // precedence states
14278            1, // rules
14279            0, // rule 0 start
14280            0, // modes
14281            0, // sets
14282            2, // transitions
14283            0, 1, 5, -1, 0, 0, // match EOF
14284            1, 2, 6, 0, 0, 0, // parser action
14285            0, // decisions
14286        ]))
14287        .deserialize_parser()
14288        .expect("artificial parser ATN should deserialize")
14289    }
14290
14291    fn noop_action_then_token_then_eof_atn() -> Atn {
14292        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14293            4, 1, 2, // version, parser, max token type
14294            4, // states
14295            2, 0, // rule start
14296            1, 0, // basic
14297            1, 0, // basic
14298            7, 0, // rule stop
14299            0, // non-greedy states
14300            0, // precedence states
14301            1, // rules
14302            0, // rule 0 start
14303            0, // modes
14304            0, // sets
14305            3, // transitions
14306            0, 1, 6, 0, -1, 0, // no-op parser action
14307            1, 2, 5, 1, 0, 0, // match token 1
14308            2, 3, 5, -1, 0, 0, // match EOF
14309            0, // decisions
14310        ]))
14311        .deserialize_parser()
14312        .expect("artificial no-op action ATN should deserialize")
14313    }
14314
14315    fn two_alt_decision_atn() -> Atn {
14316        let mut atn = ParserAtnBuilder::new(2);
14317        assert_eq!(
14318            atn.add_state(AtnStateKind::RuleStart, Some(0))
14319                .expect("state")
14320                .index(),
14321            0
14322        );
14323        assert_eq!(
14324            atn.add_state(AtnStateKind::BlockStart, Some(0))
14325                .expect("state")
14326                .index(),
14327            1
14328        );
14329        assert_eq!(
14330            atn.add_state(AtnStateKind::Basic, Some(0))
14331                .expect("state")
14332                .index(),
14333            2
14334        );
14335        assert_eq!(
14336            atn.add_state(AtnStateKind::Basic, Some(0))
14337                .expect("state")
14338                .index(),
14339            3
14340        );
14341        assert_eq!(
14342            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14343                .expect("state")
14344                .index(),
14345            4
14346        );
14347        assert_eq!(
14348            atn.add_state(AtnStateKind::RuleStop, Some(0))
14349                .expect("state")
14350                .index(),
14351            5
14352        );
14353        atn.set_rule_to_start_state(vec![0])
14354            .expect("rule start states");
14355        atn.set_rule_to_stop_state(vec![5])
14356            .expect("rule stop states");
14357        atn.add_decision_state(1).expect("decision state");
14358        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14359            .expect("transition");
14360        atn.add_transition(
14361            1,
14362            ParserTransitionSpec::Atom {
14363                target: 2,
14364                label: 1,
14365            },
14366        )
14367        .expect("transition");
14368        atn.add_transition(
14369            1,
14370            ParserTransitionSpec::Atom {
14371                target: 3,
14372                label: 2,
14373            },
14374        )
14375        .expect("transition");
14376        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
14377            .expect("transition");
14378        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14379            .expect("transition");
14380        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14381            .expect("transition");
14382        finish_atn(atn)
14383    }
14384
14385    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
14386    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
14387    fn optional_then_b_eof_atn() -> Atn {
14388        let mut atn = ParserAtnBuilder::new(3);
14389        assert_eq!(
14390            atn.add_state(AtnStateKind::RuleStart, Some(0))
14391                .expect("state")
14392                .index(),
14393            0
14394        );
14395        assert_eq!(
14396            atn.add_state(AtnStateKind::BlockStart, Some(0))
14397                .expect("state")
14398                .index(),
14399            1
14400        );
14401        assert_eq!(
14402            atn.add_state(AtnStateKind::Basic, Some(0))
14403                .expect("state")
14404                .index(),
14405            2
14406        );
14407        assert_eq!(
14408            atn.add_state(AtnStateKind::Basic, Some(0))
14409                .expect("state")
14410                .index(),
14411            3
14412        );
14413        assert_eq!(
14414            atn.add_state(AtnStateKind::Basic, Some(0))
14415                .expect("state")
14416                .index(),
14417            4
14418        );
14419        assert_eq!(
14420            atn.add_state(AtnStateKind::RuleStop, Some(0))
14421                .expect("state")
14422                .index(),
14423            5
14424        );
14425        atn.set_rule_to_start_state(vec![0])
14426            .expect("rule start states");
14427        atn.set_rule_to_stop_state(vec![5])
14428            .expect("rule stop states");
14429        atn.add_decision_state(1).expect("decision state");
14430        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14431            .expect("transition");
14432        // Optional block: match A then fall through, or skip straight to state 3.
14433        atn.add_transition(
14434            1,
14435            ParserTransitionSpec::Atom {
14436                target: 3,
14437                label: 1,
14438            },
14439        )
14440        .expect("transition");
14441        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14442            .expect("transition");
14443        // Match B, then EOF.
14444        atn.add_transition(
14445            3,
14446            ParserTransitionSpec::Atom {
14447                target: 4,
14448                label: 2,
14449            },
14450        )
14451        .expect("transition");
14452        atn.add_transition(
14453            4,
14454            ParserTransitionSpec::Atom {
14455                target: 5,
14456                label: TOKEN_EOF,
14457            },
14458        )
14459        .expect("transition");
14460        finish_atn(atn)
14461    }
14462
14463    #[test]
14464    fn sync_decision_deletes_only_a_single_token() {
14465        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
14466        // expected. `(A)? B EOF` at the optional-block decision:
14467        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
14468        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
14469        //               without consuming and records the expected set for the
14470        //               subsequent mismatch (the parser must not over-consume both
14471        //               `C`s and accept the input).
14472        let atn = optional_then_b_eof_atn();
14473
14474        let mut single = mini_parser(vec![
14475            TestToken::new(3).with_text("c"),
14476            TestToken::new(2).with_text("b"),
14477            TestToken::eof("parser-test", 1, 2, 2),
14478        ]);
14479        single.rule_context_stack = vec![RuleContextFrame {
14480            rule_index: 0,
14481            invoking_state: 0,
14482        }];
14483        let children = single
14484            .sync_decision(&atn, 1, true, false)
14485            .expect("single extraneous token recovers");
14486        assert_eq!(children.len(), 1);
14487        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
14488        assert_eq!(single.number_of_syntax_errors(), 1);
14489        // Exactly one token consumed (the cursor now sits on `b`).
14490        assert_eq!(single.la(1), 2);
14491
14492        let mut double = mini_parser(vec![
14493            TestToken::new(3).with_text("c"),
14494            TestToken::new(3).with_text("c"),
14495            TestToken::new(2).with_text("b"),
14496            TestToken::eof("parser-test", 1, 3, 3),
14497        ]);
14498        double.rule_context_stack = vec![RuleContextFrame {
14499            rule_index: 0,
14500            invoking_state: 0,
14501        }];
14502        let result = double.sync_decision(&atn, 1, true, false);
14503        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
14504        // consume either `c`. It reports the mismatch at the first `c` (so the parser
14505        // does not over-consume both and accept the input). Nothing is consumed, so
14506        // the cursor still sits on the first `c` for rule-level recovery.
14507        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
14508        match error {
14509            AntlrError::ParserError { message, .. } => {
14510                assert!(message.starts_with("mismatched input"), "got: {message}");
14511            }
14512            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
14513        }
14514        assert_eq!(double.la(1), 3);
14515    }
14516
14517    /// The real serialized ATN that `antlr4-rust-gen` emits for
14518    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
14519    /// the loop is `EOF`. The loop decision is state 5.
14520    fn star_loop_then_eof_atn() -> Atn {
14521        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14522            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,
14523            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,
14524            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,
14525            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
14526        ]))
14527        .deserialize_parser()
14528        .expect("star-loop-then-EOF ATN should deserialize")
14529    }
14530
14531    /// ATN for `s : a+ Y ; a : X ;`.
14532    ///
14533    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
14534    /// `+` loop must not treat that zero-width child as a successful iteration
14535    /// and then re-enter the loop at the same token index.
14536    fn plus_loop_with_recovering_body_atn() -> Atn {
14537        let mut atn = ParserAtnBuilder::new(2);
14538        assert_eq!(
14539            atn.add_state(AtnStateKind::RuleStart, Some(0))
14540                .expect("state")
14541                .index(),
14542            0
14543        );
14544        assert_eq!(
14545            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
14546                .expect("state")
14547                .index(),
14548            1
14549        );
14550        assert_eq!(
14551            atn.add_state(AtnStateKind::Basic, Some(0))
14552                .expect("state")
14553                .index(),
14554            2
14555        );
14556        assert_eq!(
14557            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14558                .expect("state")
14559                .index(),
14560            3
14561        );
14562        assert_eq!(
14563            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
14564                .expect("state")
14565                .index(),
14566            4
14567        );
14568        assert_eq!(
14569            atn.add_state(AtnStateKind::LoopEnd, Some(0))
14570                .expect("state")
14571                .index(),
14572            5
14573        );
14574        assert_eq!(
14575            atn.add_state(AtnStateKind::RuleStop, Some(0))
14576                .expect("state")
14577                .index(),
14578            6
14579        );
14580        assert_eq!(
14581            atn.add_state(AtnStateKind::RuleStart, Some(1))
14582                .expect("state")
14583                .index(),
14584            7
14585        );
14586        assert_eq!(
14587            atn.add_state(AtnStateKind::Basic, Some(1))
14588                .expect("state")
14589                .index(),
14590            8
14591        );
14592        assert_eq!(
14593            atn.add_state(AtnStateKind::RuleStop, Some(1))
14594                .expect("state")
14595                .index(),
14596            9
14597        );
14598        atn.set_rule_to_start_state(vec![0, 7])
14599            .expect("rule start states");
14600        atn.set_rule_to_stop_state(vec![6, 9])
14601            .expect("rule stop states");
14602        atn.set_end_state(1, 3).expect("block end state");
14603        atn.set_loop_back_state(5, 4).expect("loop back state");
14604        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14605            .expect("transition");
14606        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14607            .expect("transition");
14608        atn.add_transition(
14609            2,
14610            ParserTransitionSpec::Rule {
14611                target: 7,
14612                rule_index: 1,
14613                follow_state: 3,
14614                precedence: 0,
14615            },
14616        )
14617        .expect("transition");
14618        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14619            .expect("transition");
14620        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
14621            .expect("transition");
14622        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14623            .expect("transition");
14624        atn.add_transition(
14625            5,
14626            ParserTransitionSpec::Atom {
14627                target: 6,
14628                label: 2,
14629            },
14630        )
14631        .expect("transition");
14632        atn.add_transition(
14633            7,
14634            ParserTransitionSpec::Atom {
14635                target: 8,
14636                label: 1,
14637            },
14638        )
14639        .expect("transition");
14640        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14641            .expect("transition");
14642        finish_atn(atn)
14643    }
14644
14645    #[test]
14646    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
14647        let atn = plus_loop_with_recovering_body_atn();
14648        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14649
14650        let error = parser
14651            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
14652            .expect_err("EOF recovery should report a bounded mismatch");
14653
14654        let AntlrError::ParserError { message, .. } = error else {
14655            panic!("expected ParserError, got {error:?}");
14656        };
14657        insta::assert_snapshot!(message, @"mismatched input '<EOF>' expecting {'x', 2}");
14658        assert_eq!(parser.number_of_syntax_errors(), 1);
14659        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
14660    }
14661
14662    #[test]
14663    fn sync_decision_deletes_token_before_eof_at_loop_back() {
14664        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
14665        // At the loop ENTRY (loop_back = false) a single unexpected token before
14666        // EOF is deleted as an error node (then the generated EOF match consumes
14667        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
14668        // EOF must be a valid scan-stop for this to fire.
14669        let atn = star_loop_then_eof_atn();
14670        let mut parser = mini_parser(vec![
14671            TestToken::new(2).with_text("c"),
14672            TestToken::eof("parser-test", 1, 1, 1),
14673        ]);
14674        parser.rule_context_stack = vec![RuleContextFrame {
14675            rule_index: 0,
14676            invoking_state: 0,
14677        }];
14678        let children = parser
14679            .sync_decision(&atn, 5, true, false)
14680            .expect("single token before EOF recovers");
14681        assert_eq!(children.len(), 1);
14682        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
14683        assert_eq!(parser.number_of_syntax_errors(), 1);
14684        assert_eq!(
14685            parser.la(1),
14686            TOKEN_EOF,
14687            "EOF is left for the rule's EOF match"
14688        );
14689    }
14690
14691    #[test]
14692    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
14693        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
14694        // single-token deletion, which fails because LA(2) = `c` is not expected —
14695        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
14696        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
14697        let atn = star_loop_then_eof_atn();
14698        let mut parser = mini_parser(vec![
14699            TestToken::new(2).with_text("c"),
14700            TestToken::new(2).with_text("c"),
14701            TestToken::eof("parser-test", 1, 2, 2),
14702        ]);
14703        parser.rule_context_stack = vec![RuleContextFrame {
14704            rule_index: 0,
14705            invoking_state: 0,
14706        }];
14707        let error = parser
14708            .sync_decision(&atn, 5, true, false)
14709            .expect_err("two tokens at the loop entry must not be deleted");
14710        match error {
14711            AntlrError::ParserError { message, .. } => {
14712                assert!(message.starts_with("mismatched input"), "got: {message}");
14713            }
14714            other => panic!("expected mismatched-input ParserError, got {other:?}"),
14715        }
14716        assert_eq!(
14717            parser.la(1),
14718            2,
14719            "nothing consumed; cursor still on first `c`"
14720        );
14721    }
14722
14723    #[test]
14724    fn sync_decision_consumes_until_eof_at_loop_back() {
14725        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
14726        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
14727        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
14728        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
14729        // post-`a` state directly: `c c <EOF>` with loop_back = true.
14730        let atn = star_loop_then_eof_atn();
14731        let mut parser = mini_parser(vec![
14732            TestToken::new(2).with_text("c"),
14733            TestToken::new(2).with_text("c"),
14734            TestToken::eof("parser-test", 1, 2, 2),
14735        ]);
14736        parser.rule_context_stack = vec![RuleContextFrame {
14737            rule_index: 0,
14738            invoking_state: 0,
14739        }];
14740        let children = parser
14741            .sync_decision(&atn, 5, false, true)
14742            .expect("loop-back multi-token deletion recovers onto EOF");
14743        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
14744        assert!(
14745            children
14746                .iter()
14747                .all(|child| parser.node(*child).kind() == NodeKind::Error)
14748        );
14749        assert_eq!(parser.number_of_syntax_errors(), 1);
14750        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
14751    }
14752
14753    fn predicate_after_token_atn() -> Atn {
14754        let mut atn = ParserAtnBuilder::new(2);
14755        assert_eq!(
14756            atn.add_state(AtnStateKind::RuleStart, Some(0))
14757                .expect("state")
14758                .index(),
14759            0
14760        );
14761        assert_eq!(
14762            atn.add_state(AtnStateKind::Basic, Some(0))
14763                .expect("state")
14764                .index(),
14765            1
14766        );
14767        assert_eq!(
14768            atn.add_state(AtnStateKind::Basic, Some(0))
14769                .expect("state")
14770                .index(),
14771            2
14772        );
14773        assert_eq!(
14774            atn.add_state(AtnStateKind::Basic, Some(0))
14775                .expect("state")
14776                .index(),
14777            3
14778        );
14779        assert_eq!(
14780            atn.add_state(AtnStateKind::RuleStop, Some(0))
14781                .expect("state")
14782                .index(),
14783            4
14784        );
14785        atn.set_rule_to_start_state(vec![0])
14786            .expect("rule start states");
14787        atn.set_rule_to_stop_state(vec![4])
14788            .expect("rule stop states");
14789        atn.add_transition(
14790            0,
14791            ParserTransitionSpec::Atom {
14792                target: 1,
14793                label: 1,
14794            },
14795        )
14796        .expect("transition");
14797        atn.add_transition(
14798            1,
14799            ParserTransitionSpec::Predicate {
14800                target: 2,
14801                rule_index: 0,
14802                pred_index: 0,
14803                context_dependent: false,
14804            },
14805        )
14806        .expect("transition");
14807        atn.add_transition(
14808            2,
14809            ParserTransitionSpec::Atom {
14810                target: 3,
14811                label: 2,
14812            },
14813        )
14814        .expect("transition");
14815        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14816            .expect("transition");
14817        finish_atn(atn)
14818    }
14819
14820    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
14821        let mut atn = ParserAtnBuilder::new(1);
14822        for (state_number, kind) in [
14823            (0, AtnStateKind::RuleStart),
14824            (1, AtnStateKind::BlockStart),
14825            (2, AtnStateKind::Basic),
14826            (3, AtnStateKind::Basic),
14827            (4, AtnStateKind::Basic),
14828            (5, AtnStateKind::Basic),
14829            (6, AtnStateKind::BlockEnd),
14830            (7, AtnStateKind::RuleStop),
14831        ] {
14832            assert_eq!(
14833                atn.add_state(kind, Some(0)).expect("state").index(),
14834                state_number
14835            );
14836        }
14837        atn.set_rule_to_start_state(vec![0])
14838            .expect("rule start states");
14839        atn.set_rule_to_stop_state(vec![7])
14840            .expect("rule stop states");
14841        atn.add_decision_state(1).expect("decision state");
14842        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14843            .expect("transition");
14844        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14845            .expect("transition");
14846        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14847            .expect("transition");
14848        atn.add_transition(
14849            2,
14850            ParserTransitionSpec::Predicate {
14851                target: 4,
14852                rule_index: 0,
14853                pred_index: pred_indexes[0],
14854                context_dependent: false,
14855            },
14856        )
14857        .expect("transition");
14858        atn.add_transition(
14859            3,
14860            ParserTransitionSpec::Predicate {
14861                target: 5,
14862                rule_index: 0,
14863                pred_index: pred_indexes[1],
14864                context_dependent: false,
14865            },
14866        )
14867        .expect("transition");
14868        atn.add_transition(
14869            4,
14870            ParserTransitionSpec::Atom {
14871                target: 6,
14872                label: 1,
14873            },
14874        )
14875        .expect("transition");
14876        atn.add_transition(
14877            5,
14878            ParserTransitionSpec::Atom {
14879                target: 6,
14880                label: 1,
14881            },
14882        )
14883        .expect("transition");
14884        atn.add_transition(
14885            6,
14886            ParserTransitionSpec::Atom {
14887                target: 7,
14888                label: TOKEN_EOF,
14889            },
14890        )
14891        .expect("transition");
14892        finish_atn(atn)
14893    }
14894
14895    fn nested_nullable_context_atn() -> Atn {
14896        let mut atn = ParserAtnBuilder::new(1);
14897        for state_number in 0..=20 {
14898            let kind = match state_number {
14899                0 | 10 | 16 => AtnStateKind::RuleStart,
14900                9 | 15 | 20 => AtnStateKind::RuleStop,
14901                _ => AtnStateKind::Basic,
14902            };
14903            let rule_index = match state_number {
14904                0..=9 => 0,
14905                10..=15 => 1,
14906                _ => 2,
14907            };
14908            assert_eq!(
14909                atn.add_state(kind, Some(rule_index))
14910                    .expect("state")
14911                    .index(),
14912                state_number
14913            );
14914        }
14915        atn.set_rule_to_start_state(vec![0, 10, 16])
14916            .expect("rule start states");
14917        atn.set_rule_to_stop_state(vec![9, 15, 20])
14918            .expect("rule stop states");
14919        atn.add_transition(
14920            1,
14921            ParserTransitionSpec::Rule {
14922                target: 10,
14923                rule_index: 1,
14924                follow_state: 8,
14925                precedence: 0,
14926            },
14927        )
14928        .expect("transition");
14929        atn.add_transition(
14930            8,
14931            ParserTransitionSpec::Atom {
14932                target: 9,
14933                label: 1,
14934            },
14935        )
14936        .expect("transition");
14937        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14938            .expect("transition");
14939        atn.add_transition(
14940            2,
14941            ParserTransitionSpec::Rule {
14942                target: 16,
14943                rule_index: 2,
14944                follow_state: 14,
14945                precedence: 0,
14946            },
14947        )
14948        .expect("transition");
14949        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
14950            .expect("transition");
14951        finish_atn(atn)
14952    }
14953
14954    fn generated_match_recovery_atn() -> Atn {
14955        let mut atn = ParserAtnBuilder::new(2);
14956        assert_eq!(
14957            atn.add_state(AtnStateKind::RuleStart, Some(0))
14958                .expect("state")
14959                .index(),
14960            0
14961        );
14962        assert_eq!(
14963            atn.add_state(AtnStateKind::Basic, Some(0))
14964                .expect("state")
14965                .index(),
14966            1
14967        );
14968        assert_eq!(
14969            atn.add_state(AtnStateKind::Basic, Some(0))
14970                .expect("state")
14971                .index(),
14972            2
14973        );
14974        assert_eq!(
14975            atn.add_state(AtnStateKind::RuleStop, Some(0))
14976                .expect("state")
14977                .index(),
14978            3
14979        );
14980        assert_eq!(
14981            atn.add_state(AtnStateKind::RuleStart, Some(1))
14982                .expect("state")
14983                .index(),
14984            4
14985        );
14986        assert_eq!(
14987            atn.add_state(AtnStateKind::RuleStop, Some(1))
14988                .expect("state")
14989                .index(),
14990            5
14991        );
14992        atn.set_rule_to_start_state(vec![0, 4])
14993            .expect("rule start states");
14994        atn.set_rule_to_stop_state(vec![3, 5])
14995            .expect("rule stop states");
14996        atn.add_transition(
14997            1,
14998            ParserTransitionSpec::Rule {
14999                target: 4,
15000                rule_index: 1,
15001                follow_state: 2,
15002                precedence: 0,
15003            },
15004        )
15005        .expect("transition");
15006        atn.add_transition(
15007            2,
15008            ParserTransitionSpec::Atom {
15009                target: 3,
15010                label: TOKEN_EOF,
15011            },
15012        )
15013        .expect("transition");
15014        finish_atn(atn)
15015    }
15016
15017    fn complement_set_atn() -> Atn {
15018        let mut atn = ParserAtnBuilder::new(1);
15019        assert_eq!(
15020            atn.add_state(AtnStateKind::RuleStart, Some(0))
15021                .expect("state")
15022                .index(),
15023            0
15024        );
15025        assert_eq!(
15026            atn.add_state(AtnStateKind::RuleStop, Some(0))
15027                .expect("state")
15028                .index(),
15029            1
15030        );
15031        atn.set_rule_to_start_state(vec![0])
15032            .expect("rule start states");
15033        atn.set_rule_to_stop_state(vec![1])
15034            .expect("rule stop states");
15035        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
15036        atn.add_transition(
15037            0,
15038            ParserTransitionSpec::NotSet {
15039                target: 1,
15040                set: excluded,
15041            },
15042        )
15043        .expect("transition");
15044        finish_atn(atn)
15045    }
15046
15047    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
15048    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
15049    fn wildcard_then_eof_atn() -> Atn {
15050        let mut atn = ParserAtnBuilder::new(1);
15051        assert_eq!(
15052            atn.add_state(AtnStateKind::RuleStart, Some(0))
15053                .expect("state")
15054                .index(),
15055            0
15056        );
15057        assert_eq!(
15058            atn.add_state(AtnStateKind::RuleStop, Some(0))
15059                .expect("state")
15060                .index(),
15061            1
15062        );
15063        assert_eq!(
15064            atn.add_state(AtnStateKind::Basic, Some(0))
15065                .expect("state")
15066                .index(),
15067            2
15068        );
15069        atn.set_rule_to_start_state(vec![0])
15070            .expect("rule start states");
15071        atn.set_rule_to_stop_state(vec![1])
15072            .expect("rule stop states");
15073        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
15074            .expect("transition");
15075        atn.add_transition(
15076            2,
15077            ParserTransitionSpec::Atom {
15078                target: 1,
15079                label: TOKEN_EOF,
15080            },
15081        )
15082        .expect("transition");
15083        finish_atn(atn)
15084    }
15085
15086    #[test]
15087    fn parser_matches_token_and_reports_mismatch() {
15088        let source = Source {
15089            tokens: vec![
15090                TestToken::new(1).with_text("x"),
15091                TestToken::eof("parser-test", 1, 1, 1),
15092            ],
15093            index: 0,
15094        };
15095        let data = RecognizerData::new(
15096            "Mini.g4",
15097            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15098        );
15099        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
15100        let matched = parser.match_token(1).expect("token 1 should match");
15101        assert_eq!(parser.node(matched).text(), "x");
15102        assert!(parser.match_token(1).is_err());
15103    }
15104
15105    #[test]
15106    fn parser_matches_token_sets() {
15107        let mut parser = mini_parser(vec![
15108            TestToken::new(1).with_text("x"),
15109            TestToken::eof("parser-test", 1, 1, 1),
15110        ]);
15111
15112        let matched = parser
15113            .match_set(&[(1, 1), (3, 4)])
15114            .expect("token set should match");
15115        assert_eq!(parser.node(matched).text(), "x");
15116        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
15117    }
15118
15119    #[test]
15120    fn generated_rule_api_tracks_state_and_precedence() {
15121        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15122
15123        let context = parser.enter_rule(7, 2);
15124        assert_eq!(context.rule_index(), 2);
15125        assert_eq!(parser.state(), 7);
15126        assert_eq!(
15127            parser.rule_context_stack,
15128            vec![RuleContextFrame {
15129                rule_index: 2,
15130                invoking_state: 7
15131            }]
15132        );
15133
15134        let recursive = parser.enter_recursion_rule(11, 3, 4);
15135        assert_eq!(recursive.rule_index(), 3);
15136        assert!(parser.precpred(4));
15137        assert!(parser.precpred(5));
15138        assert!(!parser.precpred(3));
15139
15140        let next = parser.push_new_recursion_context(13, 3);
15141        assert_eq!(next.invoking_state(), 13);
15142        parser.unroll_recursion_context();
15143        assert_eq!(parser.precedence_stack, vec![0]);
15144        assert_eq!(
15145            parser.rule_context_stack,
15146            vec![RuleContextFrame {
15147                rule_index: 2,
15148                invoking_state: 7
15149            }]
15150        );
15151
15152        parser.exit_rule();
15153        assert!(parser.rule_context_stack.is_empty());
15154    }
15155
15156    #[test]
15157    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
15158        let mut parser = mini_parser(vec![
15159            TestToken::new(1).with_text("x"),
15160            TestToken::eof("parser-test", 1, 1, 1),
15161        ]);
15162        let matched = parser.match_token(1).expect("token should match");
15163        assert_eq!(parser.node(matched).text(), "x");
15164        parser.record_generated_syntax_error();
15165        parser.set_int_member(7, 11);
15166        parser.set_build_parse_trees(false);
15167        parser.set_report_diagnostic_errors(true);
15168        parser.set_prediction_mode(PredictionMode::Sll);
15169        parser.set_bail_on_error(true);
15170        let _context = parser.enter_recursion_rule(9, 0, 4);
15171        parser.pending_invoking_states.push(5);
15172        parser.unknown_predicate_hits.push((0, 1));
15173        parser.unhandled_action_hits.push((0, 2));
15174
15175        parser.reset();
15176
15177        assert_eq!(parser.input.index(), 0);
15178        assert_eq!(parser.la(1), 1);
15179        assert_eq!(parser.state(), -1);
15180        assert_eq!(parser.number_of_syntax_errors(), 0);
15181        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15182        assert!(parser.rule_context_stack.is_empty());
15183        assert!(parser.pending_invoking_states.is_empty());
15184        assert_eq!(parser.precedence_stack, [0]);
15185        assert!(parser.unknown_predicate_hits.is_empty());
15186        assert!(parser.unhandled_action_hits.is_empty());
15187        assert_eq!(parser.int_member(7), Some(11));
15188        assert!(!parser.build_parse_trees());
15189        assert!(parser.report_diagnostic_errors());
15190        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
15191        assert!(parser.bail_on_error());
15192    }
15193
15194    #[test]
15195    fn set_token_stream_replaces_input_and_resets_parser() {
15196        let mut parser = mini_parser(vec![
15197            TestToken::new(1).with_text("old"),
15198            TestToken::eof("parser-test", 1, 1, 1),
15199        ]);
15200        parser.consume();
15201        parser.record_generated_syntax_error();
15202        let replacement = CommonTokenStream::new(Source {
15203            tokens: vec![
15204                TestToken::new(2).with_text("new"),
15205                TestToken::eof("parser-test", 1, 1, 1),
15206            ],
15207            index: 0,
15208        });
15209
15210        parser.set_token_stream(replacement);
15211
15212        assert_eq!(parser.input.index(), 0);
15213        assert_eq!(parser.la(1), 2);
15214        assert_eq!(parser.input.text_all(), "new");
15215        assert_eq!(parser.number_of_syntax_errors(), 0);
15216    }
15217
15218    #[test]
15219    fn active_invocation_states_exclude_the_root_frame() {
15220        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15221
15222        let _root = parser.enter_rule(0, 0);
15223        assert!(parser.active_invocation_states().is_empty());
15224
15225        let marker = parser.push_invoking_state(6);
15226        let _child = parser.enter_rule(2, 1);
15227        parser.discard_invoking_state(marker);
15228        assert_eq!(parser.active_invocation_states(), [6]);
15229
15230        let marker = parser.push_invoking_state(13);
15231        let _grandchild = parser.enter_rule(4, 2);
15232        parser.discard_invoking_state(marker);
15233        assert_eq!(parser.active_invocation_states(), [13, 6]);
15234
15235        parser.exit_rule();
15236        parser.exit_rule();
15237        parser.exit_rule();
15238    }
15239
15240    #[test]
15241    fn parser_predicates_support_token_adjacency() {
15242        let mut parser = mini_parser(vec![
15243            TestToken::new(1).with_text("=").with_span(0, 0),
15244            TestToken::new(1).with_text(">").with_span(1, 1),
15245            TestToken::eof("parser-test", 2, 1, 2),
15246        ]);
15247        parser.consume();
15248        parser.consume();
15249
15250        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
15251
15252        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15253
15254        let mut parser = mini_parser(vec![
15255            TestToken::new(1).with_text("=").with_span(0, 0),
15256            TestToken::new(1)
15257                .with_text(" ")
15258                .with_channel(HIDDEN_CHANNEL)
15259                .with_span(1, 1),
15260            TestToken::new(1).with_text(">").with_span(2, 2),
15261            TestToken::eof("parser-test", 3, 1, 3),
15262        ]);
15263        parser.consume();
15264        parser.consume();
15265
15266        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15267    }
15268
15269    #[test]
15270    fn parser_predicates_support_context_child_text_checks() {
15271        let mut parser = mini_parser(vec![
15272            TestToken::new(1).with_text("var"),
15273            TestToken::eof("parser-test", 1, 1, 1),
15274        ]);
15275        let mut context = ParserRuleContext::new(1, 0);
15276        let mut child_context = ParserRuleContext::new(2, 0);
15277        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
15278        parser.tree.add_child(&mut child_context, terminal);
15279        let child = parser.rule_node(child_context);
15280        parser.tree.add_child(&mut context, child);
15281        let predicates = [(
15282            1,
15283            0,
15284            ParserPredicate::ContextChildRuleTextNotEquals {
15285                rule_index: 2,
15286                text: "var",
15287            },
15288        )];
15289
15290        assert!(
15291            !parser.parser_semantic_predicate_matches_with_context_and_local(
15292                &predicates,
15293                1,
15294                0,
15295                &context,
15296                0,
15297            )
15298        );
15299    }
15300
15301    #[test]
15302    fn context_expected_symbols_walks_nullable_parent_contexts() {
15303        let atn = nested_nullable_context_atn();
15304        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15305        parser.rule_context_stack = vec![
15306            RuleContextFrame {
15307                rule_index: 0,
15308                invoking_state: 0,
15309            },
15310            RuleContextFrame {
15311                rule_index: 1,
15312                invoking_state: 1,
15313            },
15314            RuleContextFrame {
15315                rule_index: 2,
15316                invoking_state: 2,
15317            },
15318        ];
15319
15320        let expected = parser.context_expected_symbols(&atn);
15321
15322        assert!(expected.contains(&1));
15323        assert!(expected.contains(&TOKEN_EOF));
15324    }
15325
15326    #[test]
15327    fn prediction_context_return_states_track_rule_stack_changes() {
15328        let atn = nested_nullable_context_atn();
15329        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15330        parser.rule_context_stack = vec![
15331            RuleContextFrame {
15332                rule_index: 0,
15333                invoking_state: 0,
15334            },
15335            RuleContextFrame {
15336                rule_index: 1,
15337                invoking_state: 1,
15338            },
15339            RuleContextFrame {
15340                rule_index: 2,
15341                invoking_state: 2,
15342            },
15343        ];
15344
15345        let initial_version = parser.rule_context_version();
15346        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15347        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15348        assert_eq!(first, second);
15349        assert_eq!(parser.rule_context_version(), initial_version);
15350
15351        parser.exit_rule();
15352        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15353        assert_ne!(first, after_pop);
15354        assert_ne!(parser.rule_context_version(), initial_version);
15355    }
15356
15357    #[test]
15358    fn generated_match_token_recovers_missing_token_from_context_follow() {
15359        let atn = generated_match_recovery_atn();
15360        let data = RecognizerData::new(
15361            "Mini.g4",
15362            Vocabulary::new(
15363                [None, Some("'X'"), Some("'Y'")],
15364                [None, Some("X"), Some("Y")],
15365                [None::<&str>, None, None],
15366            ),
15367        );
15368        let mut parser = BaseParser::new(
15369            CommonTokenStream::new(Source {
15370                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15371                index: 0,
15372            }),
15373            data,
15374        );
15375        parser.rule_context_stack = vec![
15376            RuleContextFrame {
15377                rule_index: 0,
15378                invoking_state: 0,
15379            },
15380            RuleContextFrame {
15381                rule_index: 1,
15382                invoking_state: 1,
15383            },
15384        ];
15385        assert_eq!(parser.number_of_syntax_errors(), 0);
15386
15387        let node = parser
15388            .match_token_recovering(2, 5, &atn)
15389            .expect("generated match should insert missing token");
15390
15391        assert_eq!(node.children().len(), 1);
15392        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
15393        assert_eq!(
15394            node.clone()
15395                .into_child_iter()
15396                .map(|child| parser.node(child).text())
15397                .collect::<Vec<_>>(),
15398            ["<missing 'Y'>"]
15399        );
15400        // Single-token insertion synthesizes a missing token and consumes nothing,
15401        // so no EOF terminal is consumed even though lookahead is EOF.
15402        assert!(!node.consumed_eof());
15403        assert_eq!(parser.la(1), TOKEN_EOF);
15404        assert_eq!(parser.number_of_syntax_errors(), 1);
15405        assert_eq!(
15406            parser.generated_parser_diagnostics,
15407            [ParserDiagnostic {
15408                line: 1,
15409                column: 3,
15410                message: "missing 'Y' at '<EOF>'".to_owned(),
15411            }]
15412        );
15413    }
15414
15415    #[test]
15416    fn generated_match_token_counts_single_token_deletion_recovery() {
15417        let atn = generated_match_recovery_atn();
15418        let data = RecognizerData::new(
15419            "Mini.g4",
15420            Vocabulary::new(
15421                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15422                [None, Some("X"), Some("Y"), Some("Z")],
15423                [None::<&str>, None, None, None],
15424            ),
15425        );
15426        let mut parser = BaseParser::new(
15427            CommonTokenStream::new(Source {
15428                tokens: vec![
15429                    TestToken::new(3).with_text("z"),
15430                    TestToken::new(2).with_text("y"),
15431                    TestToken::eof("parser-test", 3, 1, 3),
15432                ],
15433                index: 0,
15434            }),
15435            data,
15436        );
15437
15438        let node = parser
15439            .match_token_recovering(2, 5, &atn)
15440            .expect("generated match should delete the extraneous token");
15441
15442        assert_eq!(node.children().len(), 2);
15443        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
15444        assert_eq!(parser.node(node.children()[0]).text(), "z");
15445        assert_eq!(parser.node(node.children()[1]).text(), "y");
15446        assert_eq!(
15447            node.into_child_iter()
15448                .map(|child| parser.node(child).text())
15449                .collect::<Vec<_>>(),
15450            ["z", "y"]
15451        );
15452        assert_eq!(parser.number_of_syntax_errors(), 1);
15453    }
15454
15455    #[test]
15456    fn generated_match_token_iterates_single_success_without_a_children_vec() {
15457        let atn = generated_match_recovery_atn();
15458        let data = RecognizerData::new(
15459            "Mini.g4",
15460            Vocabulary::new(
15461                [None, Some("'X'"), Some("'Y'")],
15462                [None, Some("X"), Some("Y")],
15463                [None::<&str>, None, None],
15464            ),
15465        );
15466        let mut parser = BaseParser::new(
15467            CommonTokenStream::new(Source {
15468                tokens: vec![
15469                    TestToken::new(2).with_text("y"),
15470                    TestToken::eof("parser-test", 1, 1, 1),
15471                ],
15472                index: 0,
15473            }),
15474            data,
15475        );
15476
15477        let node = parser
15478            .match_token_recovering(2, 5, &atn)
15479            .expect("generated match should consume the expected token");
15480
15481        assert_eq!(
15482            node.into_child_iter()
15483                .map(|child| parser.node(child).text())
15484                .collect::<Vec<_>>(),
15485            ["y"]
15486        );
15487        assert_eq!(parser.number_of_syntax_errors(), 0);
15488    }
15489
15490    #[test]
15491    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
15492        let atn = generated_match_recovery_atn();
15493        let data = RecognizerData::new(
15494            "Mini.g4",
15495            Vocabulary::new(
15496                [None, Some("'X'"), Some("'Y'")],
15497                [None, Some("X"), Some("Y")],
15498                [None::<&str>, None, None],
15499            ),
15500        );
15501        let mut parser = BaseParser::new(
15502            CommonTokenStream::new(Source {
15503                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15504                index: 0,
15505            }),
15506            data,
15507        );
15508        parser.rule_context_stack = vec![
15509            RuleContextFrame {
15510                rule_index: 0,
15511                invoking_state: 0,
15512            },
15513            RuleContextFrame {
15514                rule_index: 1,
15515                invoking_state: 1,
15516            },
15517        ];
15518        let marker = parser.generated_diagnostics_checkpoint();
15519
15520        let _ = parser
15521            .match_token_recovering(2, 5, &atn)
15522            .expect("generated match should insert missing token");
15523        assert_eq!(parser.number_of_syntax_errors(), 1);
15524
15525        parser.restore_generated_diagnostics(marker);
15526
15527        assert_eq!(parser.number_of_syntax_errors(), 0);
15528        assert!(parser.generated_parser_diagnostics.is_empty());
15529    }
15530
15531    #[test]
15532    fn generated_prediction_diagnostics_use_adaptive_context() {
15533        let atn = two_alt_decision_atn();
15534        let data = RecognizerData::new(
15535            "Mini.g4",
15536            Vocabulary::new(
15537                [None, Some("'x'"), Some("'y'")],
15538                [None, Some("X"), Some("Y")],
15539                [None::<&str>, None, None],
15540            ),
15541        )
15542        .with_rule_names(["s"]);
15543        let mut parser = BaseParser::new(
15544            CommonTokenStream::new(Source {
15545                tokens: vec![
15546                    TestToken::new(1)
15547                        .with_text("x")
15548                        .with_position(1, 0)
15549                        .with_span(0, 0),
15550                    TestToken::new(2)
15551                        .with_text("y")
15552                        .with_position(1, 2)
15553                        .with_span(1, 1),
15554                    TestToken::eof("parser-test", 2, 1, 3),
15555                ],
15556                index: 0,
15557            }),
15558            data,
15559        );
15560        parser.set_report_diagnostic_errors(true);
15561
15562        parser.record_generated_prediction_diagnostic(
15563            &atn,
15564            1,
15565            &ParserAtnPrediction {
15566                alt: 1,
15567                requires_full_context: true,
15568                has_semantic_context: false,
15569                diagnostic: Some(ParserAtnPredictionDiagnostic {
15570                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
15571                    start_index: 0,
15572                    sll_stop_index: 1,
15573                    ll_stop_index: 0,
15574                    conflicting_alts: vec![1, 2],
15575                    exact: false,
15576                }),
15577            },
15578        );
15579        // Ambiguities from the default LL prediction mode are non-exact, so —
15580        // matching Java's exactOnly DiagnosticErrorListener — only the
15581        // attempting-full-context line is reported. Exact-ambiguity mode
15582        // reports the ambiguity itself.
15583        parser.record_generated_prediction_diagnostic(
15584            &atn,
15585            1,
15586            &ParserAtnPrediction {
15587                alt: 1,
15588                requires_full_context: true,
15589                has_semantic_context: false,
15590                diagnostic: Some(ParserAtnPredictionDiagnostic {
15591                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
15592                    start_index: 0,
15593                    sll_stop_index: 1,
15594                    ll_stop_index: 1,
15595                    conflicting_alts: vec![1, 2],
15596                    exact: false,
15597                }),
15598            },
15599        );
15600
15601        // The full-context/context-sensitivity diagnostic trace (order + decision + input windows)
15602        // is one snapshot rather than three ParserDiagnostic literals.
15603        insta::assert_debug_snapshot!(
15604            "generated_prediction_diagnostics_use_adaptive_context",
15605            parser.generated_parser_diagnostics
15606        );
15607    }
15608
15609    #[test]
15610    fn generated_match_not_set_recovers_empty_complement_at_eof() {
15611        let atn = complement_set_atn();
15612        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15613        parser.rule_context_stack = vec![RuleContextFrame {
15614            rule_index: 0,
15615            invoking_state: 0,
15616        }];
15617
15618        let node = parser
15619            .match_not_token_set_recovering(
15620                atn.token_set(0).expect("excluded token set"),
15621                1,
15622                1,
15623                1,
15624                &atn,
15625            )
15626            .expect("empty complement should recover at EOF");
15627
15628        assert_eq!(node.children().len(), 1);
15629        // Recovery synthesizes a missing token without consuming EOF, so the
15630        // enclosing rule must not record EOF as its stop token.
15631        assert!(!node.consumed_eof());
15632        assert_eq!(parser.la(1), TOKEN_EOF);
15633        assert_eq!(
15634            parser.generated_parser_diagnostics,
15635            [ParserDiagnostic {
15636                line: 1,
15637                column: 1,
15638                message: "missing {} at '<EOF>'".to_owned(),
15639            }]
15640        );
15641    }
15642
15643    #[test]
15644    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
15645        // `start : . EOF ;` on empty input. The wildcard is modeled as an
15646        // empty-complement not-set; at EOF the follow state (the explicit EOF
15647        // match) expects EOF, so even in the start rule recovery must perform
15648        // single-token insertion (`<missing ...>`) rather than aborting — matching
15649        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
15650        let atn = wildcard_then_eof_atn();
15651        let data = RecognizerData::new(
15652            "Mini.g4",
15653            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15654        );
15655        let mut parser = BaseParser::new(
15656            CommonTokenStream::new(Source {
15657                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
15658                index: 0,
15659            }),
15660            data,
15661        );
15662        parser.rule_context_stack = vec![RuleContextFrame {
15663            rule_index: 0,
15664            invoking_state: 0,
15665        }];
15666
15667        let node = parser
15668            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
15669            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
15670
15671        // A single `<missing ...>` error node is inserted; EOF is not consumed.
15672        assert_eq!(node.children().len(), 1);
15673        assert!(!node.consumed_eof());
15674        assert!(
15675            parser
15676                .node(node.children()[0])
15677                .text()
15678                .starts_with("<missing")
15679        );
15680        assert_eq!(parser.la(1), TOKEN_EOF);
15681        assert_eq!(
15682            parser.generated_parser_diagnostics,
15683            [ParserDiagnostic {
15684                line: 1,
15685                column: 1,
15686                message: "missing 'x' at '<EOF>'".to_owned(),
15687            }]
15688        );
15689    }
15690
15691    #[test]
15692    fn generated_rule_recovery_consumes_to_parent_follow() {
15693        let atn = generated_match_recovery_atn();
15694        let data = RecognizerData::new(
15695            "Mini.g4",
15696            Vocabulary::new(
15697                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15698                [None, Some("X"), Some("Y"), Some("Z")],
15699                [None::<&str>, None, None, None],
15700            ),
15701        );
15702        let mut parser = BaseParser::new(
15703            CommonTokenStream::new(Source {
15704                tokens: vec![
15705                    TestToken::new(3).with_text("z"),
15706                    TestToken::eof("parser-test", 1, 1, 1),
15707                ],
15708                index: 0,
15709            }),
15710            data,
15711        );
15712        let _parent = parser.enter_rule(0, 0);
15713        let marker = parser.push_invoking_state(1);
15714        let mut child = parser.enter_rule(4, 1);
15715        parser.discard_invoking_state(marker);
15716
15717        parser.recover_generated_rule(
15718            &mut child,
15719            &atn,
15720            AntlrError::ParserError {
15721                line: 1,
15722                column: 0,
15723                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15724            },
15725        );
15726        let tree = parser.finish_rule(child, false);
15727
15728        assert_eq!(parser.la(1), TOKEN_EOF);
15729        assert_eq!(
15730            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
15731            "(a z)"
15732        );
15733        assert_eq!(parser.number_of_syntax_errors(), 1);
15734        assert_eq!(
15735            parser.generated_parser_diagnostics,
15736            [ParserDiagnostic {
15737                line: 1,
15738                column: 0,
15739                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15740            }]
15741        );
15742        parser.exit_rule();
15743    }
15744
15745    #[test]
15746    fn generated_rule_recovery_forces_progress_after_repeated_error_state() {
15747        let atn = nested_nullable_context_atn();
15748        let mut parser = mini_parser(vec![
15749            TestToken::new(1).with_text("x"),
15750            TestToken::eof("parser-test", 1, 1, 1),
15751        ]);
15752        parser.rule_context_stack = vec![
15753            RuleContextFrame {
15754                rule_index: 0,
15755                invoking_state: 0,
15756            },
15757            RuleContextFrame {
15758                rule_index: 1,
15759                invoking_state: 1,
15760            },
15761            RuleContextFrame {
15762                rule_index: 2,
15763                invoking_state: 2,
15764            },
15765        ];
15766        parser.set_state(20);
15767        let mut context = ParserRuleContext::new(2, 2);
15768
15769        parser.recover_generated_rule(
15770            &mut context,
15771            &atn,
15772            AntlrError::NoViableAlternative {
15773                input: "'x'".to_owned(),
15774            },
15775        );
15776        assert_eq!(parser.input.index(), 0);
15777
15778        parser.set_state(21);
15779        parser.recover_generated_rule(
15780            &mut context,
15781            &atn,
15782            AntlrError::NoViableAlternative {
15783                input: "'x'".to_owned(),
15784            },
15785        );
15786        assert_eq!(parser.input.index(), 0);
15787        assert_eq!(
15788            parser.generated_recovery_error_states,
15789            BTreeSet::from([20, 21])
15790        );
15791
15792        parser.set_state(20);
15793        parser.recover_generated_rule(
15794            &mut context,
15795            &atn,
15796            AntlrError::NoViableAlternative {
15797                input: "'x'".to_owned(),
15798            },
15799        );
15800
15801        assert_eq!(parser.input.index(), 1);
15802        assert_eq!(parser.la(1), TOKEN_EOF);
15803        assert!(context.has_matched_child());
15804        assert_eq!(parser.generated_recovery_error_states, BTreeSet::from([20]));
15805
15806        parser.match_eof().expect("EOF should match");
15807        assert_eq!(parser.generated_recovery_error_index, None);
15808        assert!(parser.generated_recovery_error_states.is_empty());
15809    }
15810
15811    #[test]
15812    fn greedy_ll1_alt_handles_nullable_loop_exit() {
15813        let mut body_symbols = TokenBitSet::default();
15814        body_symbols.insert(1);
15815        let entry = DecisionLookahead {
15816            transitions: vec![
15817                TransitionLookSet {
15818                    symbols: body_symbols,
15819                    nullable: false,
15820                },
15821                TransitionLookSet {
15822                    symbols: TokenBitSet::default(),
15823                    nullable: true,
15824                },
15825            ],
15826        };
15827
15828        assert_eq!(ll1_unique_alt(&entry, 2), None);
15829        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
15830        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
15831        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
15832    }
15833
15834    #[test]
15835    fn ordinary_repetition_builds_tree_in_input_order() {
15836        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15837            let mut parser = mini_parser(repeated_x_tokens(3));
15838            let tree = parser
15839                .parse_atn_rule(&atn, 0)
15840                .expect("ordinary repetition should parse");
15841
15842            let root = parser
15843                .node(tree)
15844                .as_rule()
15845                .expect("entry result should be a rule");
15846            let body_rules = root.child_rules(1).collect::<Vec<_>>();
15847            assert_eq!(root.text(), "xxx<EOF>");
15848            assert_eq!(body_rules.len(), 3);
15849            assert_eq!(
15850                body_rules
15851                    .iter()
15852                    .map(|rule| rule.start_id().expect("body start").index())
15853                    .collect::<Vec<_>>(),
15854                [0, 1, 2]
15855            );
15856            assert_eq!(
15857                body_rules
15858                    .iter()
15859                    .map(|rule| rule.stop_id().expect("body stop").index())
15860                    .collect::<Vec<_>>(),
15861                [0, 1, 2]
15862            );
15863            assert_eq!(parser.number_of_syntax_errors(), 0);
15864        }
15865    }
15866
15867    #[test]
15868    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
15869        const DEPTH: usize = 20_000;
15870
15871        std::thread::Builder::new()
15872            .name("deferred-rule-materialization".to_owned())
15873            .stack_size(256 * 1024)
15874            .spawn(|| {
15875                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15876                let mut root = FastDeferredNodeId::EMPTY;
15877                for depth in 0..DEPTH {
15878                    root = parser
15879                        .recognition_arena
15880                        .deferred_rule_node(FastDeferredRule {
15881                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
15882                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
15883                            start_index: 0,
15884                            stop_index: None,
15885                            deferred_children: root,
15886                            children: NodeSeqId::EMPTY,
15887                        });
15888                }
15889
15890                let (mut children, alt_number) =
15891                    parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
15892                assert_eq!(alt_number, 0);
15893                for expected_rule in (0..DEPTH).rev() {
15894                    let mut nodes = parser.recognition_arena.iter(children);
15895                    let node = nodes.next().expect("nested rule node");
15896                    assert!(nodes.next().is_none(), "each rule has one child");
15897                    let ArenaRecognizedNode::Rule {
15898                        rule_index,
15899                        children: nested,
15900                        ..
15901                    } = parser.recognition_arena.node(node)
15902                    else {
15903                        panic!("expected nested rule");
15904                    };
15905                    assert_eq!(rule_index as usize, expected_rule);
15906                    children = nested;
15907                }
15908                assert!(children.is_empty());
15909            })
15910            .expect("small-stack thread should start")
15911            .join()
15912            .expect("deferred rules should materialize without recursion");
15913    }
15914
15915    #[test]
15916    fn deferred_alternatives_preserve_left_recursive_contexts() {
15917        let mut parser = mini_parser(vec![
15918            TestToken::new(1).with_text("1"),
15919            TestToken::new(2).with_text("+"),
15920            TestToken::new(1).with_text("2"),
15921            TestToken::eof("parser-test", 3, 1, 3),
15922        ]);
15923        let base = parser.arena_token_node(0, false);
15924        let operator = parser.arena_token_node(1, false);
15925        let right = parser.arena_token_node(2, false);
15926
15927        let base = parser.recognition_arena.prepend(NodeSeqId::EMPTY, base);
15928        let base = parser.recognition_arena.deferred_fragment(base);
15929        let operator = parser.recognition_arena.prepend(NodeSeqId::EMPTY, operator);
15930        let operator = parser.recognition_arena.deferred_fragment(operator);
15931        let right = parser.recognition_arena.prepend(NodeSeqId::EMPTY, right);
15932        let right = parser.recognition_arena.deferred_fragment(right);
15933        let base_alt = parser.recognition_arena.deferred_alternative(1);
15934        let boundary = parser.recognition_arena.deferred_left_recursive_boundary(0);
15935        let operator_alt = parser.recognition_arena.deferred_alternative(6);
15936
15937        let mut deferred = FastDeferredNodeId::EMPTY;
15938        for fragment in [base_alt, base, boundary, operator_alt, operator, right] {
15939            deferred = parser
15940                .recognition_arena
15941                .concat_deferred_nodes(deferred, fragment);
15942        }
15943        let (nodes, root_alt_number) =
15944            parser.materialize_fast_deferred_nodes(deferred, NodeSeqId::EMPTY);
15945        let nodes = parser
15946            .recognition_arena
15947            .fold_left_recursive_boundaries(nodes);
15948
15949        let mut root = ParserRuleContext::new(0, -1);
15950        root.set_context_alt_number(root_alt_number);
15951        let mut cursor = nodes;
15952        while let Some(link) = parser.recognition_arena.link(cursor) {
15953            let child = parser
15954                .arena_recognized_node_tree(link.head, false, true)
15955                .expect("materialized child should become a public tree");
15956            parser.tree.add_child(&mut root, child);
15957            cursor = link.tail;
15958        }
15959        let tree = parser.rule_node(root);
15960        let contexts = parser
15961            .node(tree)
15962            .descendants()
15963            .filter_map(Node::as_rule)
15964            .map(|rule| {
15965                (
15966                    rule.rule_index(),
15967                    rule.alt_number(),
15968                    rule.context_alt_number(),
15969                    rule.text(),
15970                )
15971            })
15972            .collect::<Vec<_>>();
15973
15974        insta::assert_debug_snapshot!(
15975            "deferred_alternatives_preserve_left_recursive_contexts",
15976            contexts
15977        );
15978    }
15979
15980    #[test]
15981    fn fast_recognizer_preserves_labeled_left_recursive_operator_context() {
15982        let atn = labeled_left_recursive_operator_atn();
15983        let mut parser = mini_parser(vec![
15984            TestToken::new(1).with_text("a"),
15985            TestToken::new(3).with_text("+"),
15986            TestToken::new(1).with_text("b"),
15987            TestToken::eof("parser-test", 3, 1, 3),
15988        ]);
15989
15990        let (tree, _) = parser
15991            .parse_atn_rule_with_runtime_options(
15992                &atn,
15993                0,
15994                ParserRuntimeOptions {
15995                    track_context_alt_numbers: true,
15996                    ..ParserRuntimeOptions::default()
15997                },
15998            )
15999            .expect("labeled left-recursive addition should parse");
16000        let contexts = parser
16001            .node(tree)
16002            .descendants()
16003            .filter_map(Node::as_rule)
16004            .map(|rule| {
16005                let operator = rule
16006                    .children()
16007                    .next()
16008                    .and_then(Node::as_rule)
16009                    .is_some_and(|child| child.rule_index() == rule.rule_index());
16010                (operator, rule.context_alt_number(), rule.text())
16011            })
16012            .collect::<Vec<_>>();
16013
16014        insta::assert_debug_snapshot!(
16015            "fast_recognizer_preserves_labeled_left_recursive_operator_context",
16016            contexts
16017        );
16018        assert!(!parser.recognition_arena.deferred_nodes.is_empty());
16019        assert_eq!(parser.number_of_syntax_errors(), 0);
16020    }
16021
16022    #[test]
16023    fn deeply_nested_rule_calls_grow_the_stack() {
16024        const DEPTH: usize = 4_096;
16025        const STACK_SIZE: usize = 256 * 1024;
16026        let atn = nested_rule_chain_atn(DEPTH);
16027        std::thread::Builder::new()
16028            .name("nested-adaptive-set-rules".to_owned())
16029            .stack_size(STACK_SIZE)
16030            .spawn(move || {
16031                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16032                parser.set_build_parse_trees(false);
16033                // This test isolates recognizer depth from the separately
16034                // cached FIRST-set metadata walk.
16035                parser.fast_first_set_prefilter = false;
16036                parser
16037                    .parse_atn_rule(&atn, 0)
16038                    .expect("nested rule chain should grow the native stack");
16039                assert_eq!(parser.input.index(), 1);
16040            })
16041            .expect("small-stack thread should start")
16042            .join()
16043            .expect("nested rule chain should not overflow its stack");
16044    }
16045
16046    #[test]
16047    fn deeply_nested_branching_rules_grow_the_stack() {
16048        const DEPTH: usize = 4_096;
16049        const STACK_SIZE: usize = 256 * 1024;
16050        let atn = nested_rule_graph_atn(DEPTH, true, false);
16051        std::thread::Builder::new()
16052            .name("nested-branching-rules".to_owned())
16053            .stack_size(STACK_SIZE)
16054            .spawn(move || {
16055                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16056                parser.set_build_parse_trees(false);
16057                parser
16058                    .parse_atn_rule(&atn, 0)
16059                    .expect("branching rule chain should grow the native stack");
16060                assert_eq!(parser.input.index(), 1);
16061            })
16062            .expect("small-stack thread should start")
16063            .join()
16064            .expect("branching rule chain should not overflow its stack");
16065    }
16066
16067    #[test]
16068    fn deeply_nested_rule_follows_grow_the_stack() {
16069        const DEPTH: usize = 4_096;
16070        const STACK_SIZE: usize = 256 * 1024;
16071        let atn = nested_rule_graph_atn(DEPTH, false, true);
16072        std::thread::Builder::new()
16073            .name("nested-rule-follows".to_owned())
16074            .stack_size(STACK_SIZE)
16075            .spawn(move || {
16076                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
16077                parser.set_build_parse_trees(false);
16078                parser.fast_first_set_prefilter = false;
16079                parser
16080                    .parse_atn_rule(&atn, 0)
16081                    .expect("rule follow chain should grow the native stack");
16082                assert_eq!(parser.input.index(), DEPTH);
16083            })
16084            .expect("small-stack thread should start")
16085            .join()
16086            .expect("nested rule follow chain should not overflow its stack");
16087    }
16088
16089    #[test]
16090    fn deeply_nested_recovery_grows_the_stack() {
16091        const DEPTH: usize = 4_096;
16092        const STACK_SIZE: usize = 256 * 1024;
16093        let atn = nested_rule_chain_atn(DEPTH);
16094        std::thread::Builder::new()
16095            .name("nested-rule-recovery".to_owned())
16096            .stack_size(STACK_SIZE)
16097            .spawn(move || {
16098                let mut parser = mini_parser(vec![
16099                    TestToken::new(2).with_text("z"),
16100                    TestToken::new(1).with_text("x"),
16101                    TestToken::eof("parser-test", 2, 1, 2),
16102                ]);
16103                parser.set_build_parse_trees(false);
16104                parser.fast_first_set_prefilter = false;
16105                parser
16106                    .parse_atn_rule(&atn, 0)
16107                    .expect("nested recovery should grow the native stack");
16108                assert_eq!(parser.input.index(), 2);
16109                assert_eq!(parser.number_of_syntax_errors(), 1);
16110            })
16111            .expect("small-stack thread should start")
16112            .join()
16113            .expect("nested rule recovery should not overflow its stack");
16114    }
16115
16116    #[test]
16117    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
16118        const REPETITIONS: usize = 64;
16119
16120        let atn = ambiguous_ordinary_star_loop_atn();
16121        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16122        let tree = parser
16123            .parse_atn_rule(&atn, 0)
16124            .expect("ambiguous ordinary repetition should parse");
16125
16126        let root = parser
16127            .node(tree)
16128            .as_rule()
16129            .expect("entry result should be a rule");
16130        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
16131        assert_eq!(parser.input.index(), REPETITIONS);
16132        assert!(
16133            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
16134            "equivalent segmentations should keep deferred storage linear"
16135        );
16136        assert_eq!(parser.number_of_syntax_errors(), 0);
16137    }
16138
16139    #[test]
16140    fn long_ordinary_repetition_does_not_consume_native_stack() {
16141        const REPETITIONS: usize = 20_000;
16142
16143        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16144            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16145            parser.set_build_parse_trees(false);
16146            parser
16147                .parse_atn_rule(&atn, 0)
16148                .expect("long ordinary repetition should parse");
16149
16150            assert_eq!(parser.input.index(), REPETITIONS);
16151            assert_eq!(parser.number_of_syntax_errors(), 0);
16152        }
16153    }
16154
16155    #[test]
16156    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
16157        const REPETITIONS: usize = 2_000;
16158        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
16159
16160        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16161            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16162            let tree = parser
16163                .parse_atn_rule(&atn, 0)
16164                .expect("long rule repetition should parse");
16165
16166            let root = parser
16167                .node(tree)
16168                .as_rule()
16169                .expect("entry result should be a rule");
16170            assert_eq!(root.text(), expected_text);
16171            assert_eq!(root.child_rules(1).count(), REPETITIONS);
16172            let first_body = root.child_rules(1).next().expect("first body rule");
16173            let last_body = root.child_rules(1).next_back().expect("last body rule");
16174            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
16175            assert_eq!(
16176                last_body.stop_id().expect("last body stop").index(),
16177                REPETITIONS - 1
16178            );
16179
16180            let stats = parser.recognition_arena_stats();
16181            assert_eq!(
16182                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16183                (REPETITIONS, REPETITIONS, 0)
16184            );
16185            assert_eq!(
16186                (stats.total_links, stats.live_links, stats.dead_links),
16187                (REPETITIONS, REPETITIONS, 0)
16188            );
16189            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
16190            assert_eq!(
16191                parser.recognition_arena.deferred_nodes.len(),
16192                REPETITIONS * 2 - 1
16193            );
16194            assert_eq!(parser.number_of_syntax_errors(), 0);
16195        }
16196    }
16197
16198    #[test]
16199    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
16200        let key = |state_number| FastRecognizeKey {
16201            state_number,
16202            stop_state: 10,
16203            index: state_number,
16204            rule_start_index: 0,
16205            decision_start_index: None,
16206            precedence: 0,
16207            recovery_symbols_id: 0,
16208            recovery_state: None,
16209        };
16210
16211        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16212        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
16213            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
16214        }
16215        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
16216        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
16217
16218        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16219        let repeated = key(1);
16220        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
16221            assert!(promote.clean_memo_enabled_for_key(&repeated));
16222        }
16223        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
16224
16225        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
16226            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
16227        }
16228        assert!(sparse.clean_memo_enabled_for_key(&repeated));
16229        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
16230        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
16231            assert!(sparse.clean_memo_enabled_for_key(&repeated));
16232        }
16233        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
16234    }
16235
16236    #[test]
16237    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
16238        assert_eq!(
16239            fast_recognize_memo_capacity(0),
16240            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16241        );
16242        assert_eq!(
16243            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
16244            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16245        );
16246        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
16247        assert_eq!(
16248            fast_recognize_memo_capacity(usize::MAX),
16249            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
16250        );
16251    }
16252
16253    #[test]
16254    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
16255        let mut scratch = FastRecognizeTopScratch::default();
16256        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16257        let retained_capacity = scratch.memo.capacity();
16258        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16259        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16260
16261        let larger_capacity = retained_capacity + 1;
16262        scratch.prepare(larger_capacity);
16263        let grown_capacity = scratch.memo.capacity();
16264        assert!(grown_capacity >= larger_capacity);
16265        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16266
16267        scratch.memo.insert(
16268            FastRecognizeKey {
16269                state_number: 0,
16270                stop_state: 0,
16271                index: 0,
16272                rule_start_index: 0,
16273                decision_start_index: None,
16274                precedence: 0,
16275                recovery_symbols_id: 0,
16276                recovery_state: None,
16277            },
16278            Rc::from([FastRecognizeOutcome {
16279                index: 0,
16280                consumed_eof: false,
16281                diagnostics: DiagnosticSeqId::EMPTY,
16282                deferred_nodes: FastDeferredNodeId::EMPTY,
16283                nodes: NodeSeqId::EMPTY,
16284            }]),
16285        );
16286        scratch.release_oversized_memo();
16287        assert!(scratch.memo.is_empty());
16288        assert_eq!(scratch.memo.capacity(), grown_capacity);
16289
16290        scratch
16291            .memo
16292            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
16293        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16294
16295        scratch.release_oversized_memo();
16296        assert!(scratch.memo.is_empty());
16297        assert_eq!(scratch.memo.capacity(), 0);
16298    }
16299
16300    #[test]
16301    fn clean_empty_multi_alt_outcomes_are_memoized() {
16302        let mut atn = ParserAtnBuilder::new(2);
16303        assert_eq!(
16304            atn.add_state(AtnStateKind::RuleStart, Some(0))
16305                .expect("state")
16306                .index(),
16307            0
16308        );
16309        assert_eq!(
16310            atn.add_state(AtnStateKind::BlockStart, Some(0))
16311                .expect("state")
16312                .index(),
16313            1
16314        );
16315        assert_eq!(
16316            atn.add_state(AtnStateKind::RuleStop, Some(0))
16317                .expect("state")
16318                .index(),
16319            2
16320        );
16321        atn.set_rule_to_start_state(vec![0])
16322            .expect("rule start states");
16323        atn.set_rule_to_stop_state(vec![2])
16324            .expect("rule stop states");
16325        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16326            .expect("transition");
16327        atn.add_transition(
16328            1,
16329            ParserTransitionSpec::Atom {
16330                target: 2,
16331                label: 1,
16332            },
16333        )
16334        .expect("transition");
16335        atn.add_transition(
16336            1,
16337            ParserTransitionSpec::Atom {
16338                target: 2,
16339                label: 2,
16340            },
16341        )
16342        .expect("transition");
16343        let atn = finish_atn(atn);
16344
16345        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16346        parser.fast_recovery_enabled = false;
16347        let mut visiting = FxHashSet::default();
16348        let mut memo = FxHashMap::default();
16349        let mut expected = ExpectedTokens::default();
16350        let outcomes = parser.recognize_state_fast(
16351            &atn,
16352            FastRecognizeRequest {
16353                state_number: 1,
16354                stop_state: 2,
16355                index: 0,
16356                rule_start_index: 0,
16357                decision_start_index: None,
16358                precedence: 0,
16359                depth: 0,
16360                recovery_symbols: parser.empty_recovery_symbols(),
16361                recovery_state: None,
16362            },
16363            FastRecognizeScratch {
16364                predicate_context: None,
16365                visiting: &mut visiting,
16366                memo: &mut memo,
16367                expected: &mut expected,
16368                native_depth: 0,
16369            },
16370        );
16371
16372        assert!(outcomes.is_empty());
16373        assert_eq!(memo.len(), 1);
16374        assert!(memo.values().next().expect("memo entry").is_empty());
16375
16376        parser.clean_memo_mode = CleanMemoMode::Sparse;
16377        visiting.clear();
16378        memo.clear();
16379        expected = ExpectedTokens::default();
16380        let sparse_outcomes = parser.recognize_state_fast(
16381            &atn,
16382            FastRecognizeRequest {
16383                state_number: 1,
16384                stop_state: 2,
16385                index: 0,
16386                rule_start_index: 0,
16387                decision_start_index: None,
16388                precedence: 0,
16389                depth: 0,
16390                recovery_symbols: parser.empty_recovery_symbols(),
16391                recovery_state: None,
16392            },
16393            FastRecognizeScratch {
16394                predicate_context: None,
16395                visiting: &mut visiting,
16396                memo: &mut memo,
16397                expected: &mut expected,
16398                native_depth: 0,
16399            },
16400        );
16401
16402        assert!(sparse_outcomes.is_empty());
16403        assert!(memo.is_empty());
16404    }
16405
16406    #[test]
16407    fn wildcard_matches_non_eof_only() {
16408        let mut parser = mini_parser(vec![
16409            TestToken::new(1).with_text("x"),
16410            TestToken::eof("parser-test", 1, 1, 1),
16411        ]);
16412        let matched = parser.match_wildcard().expect("wildcard");
16413        assert_eq!(parser.node(matched).text(), "x");
16414        assert!(parser.match_wildcard().is_err());
16415    }
16416
16417    #[test]
16418    fn add_parse_child_records_match_even_without_tree_building() {
16419        // `sync_decision`'s "is the current context empty" flag must reflect real
16420        // matches, not parse-tree children: when `build_parse_trees(false)`,
16421        // `children` stays empty but `has_matched_child` must still flip so nested
16422        // recovery does not wrongly suppress single-token deletion.
16423        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16424        let token = TestToken::new(1).with_text("x");
16425
16426        parser.set_build_parse_trees(false);
16427        let mut ctx = ParserRuleContext::new(0, 0);
16428        assert!(!ctx.has_matched_child());
16429        let child = parser.terminal_tree(token.id);
16430        parser.add_parse_child(&mut ctx, child);
16431        // Tree building is off, so no child is stored...
16432        assert_eq!(ctx.child_count(), 0);
16433        assert_eq!(parser.parse_tree_storage().node_count(), 0);
16434        // ...but the match is recorded, so the context is no longer "empty".
16435        assert!(ctx.has_matched_child());
16436
16437        // With tree building on, the child is stored and the match is recorded.
16438        parser.set_build_parse_trees(true);
16439        let mut ctx = ParserRuleContext::new(0, 0);
16440        let child = parser.terminal_tree(token.id);
16441        parser.add_parse_child(&mut ctx, child);
16442        assert_eq!(ctx.child_count(), 1);
16443        assert!(ctx.has_matched_child());
16444    }
16445
16446    #[test]
16447    fn disabled_tree_building_does_not_grow_flat_storage() {
16448        let mut parser = mini_parser(vec![
16449            TestToken::new(1).with_text("x"),
16450            TestToken::new(1).with_text("y"),
16451            TestToken::eof("parser-test", 2, 1, 2),
16452        ]);
16453        parser.set_build_parse_trees(false);
16454        let mut context = ParserRuleContext::new(0, -1);
16455
16456        for _ in 0..2 {
16457            let child = parser.match_token(1).expect("token should match");
16458            parser.add_parse_child(&mut context, child);
16459        }
16460        let current = parser.input.lt_id(1).expect("EOF token");
16461        let error = parser.error_tree(current);
16462        parser.add_parse_child(&mut context, error);
16463        let root = parser.rule_node(context);
16464
16465        assert_eq!(
16466            parser.parse_tree_storage().stats(),
16467            ParseTreeStats::default()
16468        );
16469        assert!(
16470            parser
16471                .parse_tree_storage()
16472                .node(parser.token_store(), root)
16473                .is_none(),
16474            "the no-tree sentinel must not resolve to stored data"
16475        );
16476    }
16477
16478    #[test]
16479    fn disabled_tree_building_skips_recognition_rule_node_storage() {
16480        let atn = ordinary_star_loop_atn();
16481        let mut parser = mini_parser(repeated_x_tokens(3));
16482        parser.set_build_parse_trees(false);
16483
16484        parser
16485            .parse_atn_rule(&atn, 0)
16486            .expect("ordinary repetition should parse without a tree");
16487
16488        assert_eq!(parser.input.index(), 3);
16489        assert!(parser.recognition_arena.nodes.is_empty());
16490        assert!(parser.recognition_arena.seq_links.is_empty());
16491        assert!(parser.recognition_arena.deferred_nodes.is_empty());
16492        assert!(parser.recognition_arena.deferred_rules.is_empty());
16493        assert!(!parser.fast_token_nodes_enabled);
16494        assert!(parser.fast_recognize_scratch.memo.is_empty());
16495    }
16496
16497    #[test]
16498    fn parser_interprets_simple_atn_rule() {
16499        let atn = token_then_eof_atn();
16500        let mut parser = mini_parser(vec![
16501            TestToken::new(1).with_text("x"),
16502            TestToken::eof("parser-test", 1, 1, 1),
16503        ]);
16504
16505        let tree = parser
16506            .parse_atn_rule(&atn, 0)
16507            .expect("artificial parser rule should parse");
16508        assert_eq!(parser.node(tree).text(), "x<EOF>");
16509        assert_eq!(parser.number_of_syntax_errors(), 0);
16510        assert_eq!(
16511            parser
16512                .node(tree)
16513                .first_rule_stop(0)
16514                .expect("rule should stop at EOF")
16515                .token_type(),
16516            TOKEN_EOF
16517        );
16518
16519        let mut parser = mini_parser(vec![
16520            TestToken::new(1).with_text("x"),
16521            TestToken::eof("parser-test", 1, 1, 1),
16522        ]);
16523        let (tree, actions) = parser
16524            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16525            .expect("runtime-option parser rule should parse");
16526        assert!(actions.is_empty());
16527        assert_eq!(
16528            parser
16529                .node(tree)
16530                .first_rule_stop(0)
16531                .expect("rule should stop at EOF")
16532                .token_type(),
16533            TOKEN_EOF
16534        );
16535    }
16536
16537    #[test]
16538    fn runtime_options_default_ignores_noop_action_transitions() {
16539        let atn = noop_action_then_token_then_eof_atn();
16540        let mut parser = mini_parser(vec![
16541            TestToken::new(1).with_text("x"),
16542            TestToken::eof("parser-test", 1, 1, 1),
16543        ]);
16544
16545        let (tree, actions) = parser
16546            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16547            .expect("no-op parser action should not force action replay");
16548
16549        assert_eq!(parser.node(tree).text(), "x<EOF>");
16550        assert!(
16551            actions.is_empty(),
16552            "action_index=None transitions are ANTLR metadata, not replay actions"
16553        );
16554        assert_eq!(parser.number_of_syntax_errors(), 0);
16555    }
16556
16557    #[test]
16558    fn parser_exposes_buffered_token_stream_after_parse() {
16559        let atn = token_then_eof_atn();
16560        let mut parser = mini_parser(vec![
16561            TestToken::new(1).with_text("x"),
16562            TestToken::eof("parser-test", 1, 1, 1),
16563        ]);
16564
16565        let tree = parser
16566            .parse_atn_rule(&atn, 0)
16567            .expect("artificial parser rule should parse");
16568        assert_eq!(parser.node(tree).text(), "x<EOF>");
16569
16570        let stream = parser.token_stream();
16571        let source_index_after_parse = stream.token_source().index;
16572        let buffered = stream.tokens().collect::<Vec<_>>();
16573        assert_eq!(buffered.len(), 2);
16574        assert_eq!(buffered[0].text(), Some("x"));
16575        assert_eq!(buffered[0].token_id().index(), 0);
16576        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
16577        assert_eq!(stream.token_source().index, source_index_after_parse);
16578        drop(buffered);
16579
16580        let stream = parser.into_token_stream();
16581        assert_eq!(stream.token_source().index, source_index_after_parse);
16582        assert_eq!(
16583            stream.tokens().next().expect("first token").text(),
16584            Some("x")
16585        );
16586        assert_eq!(
16587            stream.tokens().nth(1).expect("EOF token").token_type(),
16588            TOKEN_EOF
16589        );
16590    }
16591
16592    #[test]
16593    fn parsed_file_exposes_all_buffered_tokens() {
16594        let atn = token_then_eof_atn();
16595        let mut parser = mini_parser(vec![
16596            TestToken::new(99)
16597                .with_text(" comment")
16598                .with_channel(HIDDEN_CHANNEL),
16599            TestToken::new(1).with_text("x"),
16600            TestToken::eof("parser-test", 9, 1, 9),
16601        ]);
16602
16603        let tree = parser
16604            .parse_atn_rule(&atn, 0)
16605            .expect("artificial parser rule should parse");
16606        let parsed = parser.into_parsed_file(tree);
16607
16608        // Snapshot the full buffered stream — hidden-channel comment, default-channel token, EOF —
16609        // as (type, channel, text) triples; contents make the count self-evident.
16610        insta::assert_debug_snapshot!(
16611            "parsed_file_exposes_all_buffered_tokens",
16612            parsed
16613                .tokens()
16614                .iter()
16615                .map(|token| (token.token_type(), token.channel(), token.text()))
16616                .collect::<Vec<_>>()
16617        );
16618        assert_eq!(parsed.tokens().into_iter().count(), 3);
16619    }
16620
16621    #[test]
16622    fn parser_syntax_error_count_tracks_interpreted_recovery() {
16623        let atn = token_then_eof_atn();
16624        let mut parser = mini_parser(vec![
16625            TestToken::new(1).with_text("x"),
16626            TestToken::new(2).with_text("y"),
16627            TestToken::eof("parser-test", 2, 1, 2),
16628        ]);
16629
16630        let tree = parser
16631            .parse_atn_rule(&atn, 0)
16632            .expect("invalid token should recover into an error node");
16633
16634        assert_eq!(parser.number_of_syntax_errors(), 1);
16635        assert_eq!(
16636            parser
16637                .node(tree)
16638                .first_error_token()
16639                .expect("recovery should embed an error token")
16640                .text(),
16641            Some("y")
16642        );
16643    }
16644
16645    #[test]
16646    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
16647        let atn = token_then_eof_atn();
16648        let mut parser = mini_parser(vec![
16649            TestToken::new(2).with_text("y"),
16650            TestToken::eof("parser-test", 1, 1, 1),
16651        ]);
16652
16653        let error = parser
16654            .parse_atn_rule(&atn, 0)
16655            .expect_err("start-rule mismatch should remain a parser error");
16656
16657        assert_eq!(parser.number_of_syntax_errors(), 1);
16658        assert!(matches!(error, AntlrError::ParserError { .. }));
16659    }
16660
16661    #[test]
16662    fn adaptive_direct_rule_uses_simulator_decision() {
16663        let atn = two_alt_decision_atn();
16664        let mut simulator = ParserAtnSimulator::new(&atn);
16665        let mut parser = mini_parser(vec![
16666            TestToken::new(2).with_text("y"),
16667            TestToken::eof("parser-test", 1, 1, 1),
16668        ]);
16669
16670        let tree = parser
16671            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
16672            .expect("direct adaptive rule should parse");
16673
16674        assert_eq!(parser.node(tree).text(), "y");
16675        assert_eq!(parser.input.index(), 1);
16676    }
16677
16678    #[test]
16679    fn adaptive_direct_rule_restores_input_on_fallback() {
16680        let atn = predicate_after_token_atn();
16681        let mut simulator = ParserAtnSimulator::new(&atn);
16682        let mut parser = mini_parser(vec![
16683            TestToken::new(1).with_text("x"),
16684            TestToken::new(2).with_text("y"),
16685            TestToken::eof("parser-test", 2, 1, 2),
16686        ]);
16687
16688        let tree = parser
16689            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
16690            .expect("fallback recognizer should parse");
16691
16692        assert_eq!(parser.node(tree).text(), "xy");
16693        assert_eq!(parser.input.index(), 2);
16694        let stats = parser.parse_tree_storage().stats();
16695        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
16696        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
16697        assert_eq!(stats.scratch_links, 0);
16698    }
16699
16700    #[test]
16701    fn unknown_predicate_policy_defaults_to_assume_true() {
16702        let atn = predicate_after_token_atn();
16703        let mut parser = mini_parser(vec![
16704            TestToken::new(1).with_text("x"),
16705            TestToken::new(2).with_text("y"),
16706            TestToken::eof("parser-test", 2, 1, 2),
16707        ]);
16708
16709        let (tree, _) = parser
16710            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16711            .expect("unknown predicate should pass under the default policy");
16712
16713        assert_eq!(parser.node(tree).text(), "xy");
16714        assert_eq!(parser.number_of_syntax_errors(), 0);
16715    }
16716
16717    #[test]
16718    fn private_context_alt_tracking_keeps_fast_predicate_recognition() {
16719        let atn = predicate_gated_same_lookahead_atn([0, 1]);
16720        let mut parser = mini_parser(vec![
16721            TestToken::new(1).with_text("x"),
16722            TestToken::eof("parser-test", 1, 1, 1),
16723        ]);
16724
16725        let (tree, _) = parser
16726            .parse_atn_rule_with_runtime_options(
16727                &atn,
16728                0,
16729                ParserRuntimeOptions {
16730                    predicates: &[
16731                        (0, 0, ParserPredicate::False),
16732                        (0, 1, ParserPredicate::True),
16733                    ],
16734                    track_context_alt_numbers: true,
16735                    ..ParserRuntimeOptions::default()
16736                },
16737            )
16738            .expect("the second predicate-gated alternative should match");
16739
16740        let root = parser.node(tree).as_rule().expect("entry result is a rule");
16741        insta::assert_debug_snapshot!(
16742            "private_context_alt_tracking_keeps_fast_predicate_recognition",
16743            (root.alt_number(), root.context_alt_number(), root.text())
16744        );
16745        assert_eq!(parser.number_of_syntax_errors(), 0);
16746        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
16747        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
16748    }
16749
16750    #[test]
16751    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
16752        // A generated parent may record an unknown-predicate coordinate, then
16753        // descend into an interpreted child. The child's interpreter entry must
16754        // not wipe the parent's recorded hit before the top-level surfaces it.
16755        let atn = token_then_eof_atn();
16756        let mut parser = mini_parser(vec![
16757            TestToken::new(1).with_text("x"),
16758            TestToken::eof("parser-test", 1, 1, 1),
16759        ]);
16760
16761        // Simulate the parent having recorded a fail-loud coordinate.
16762        parser.unknown_predicate_hits.push((7, 3));
16763
16764        // Run an interpreted child parse that records no coordinate of its own.
16765        parser
16766            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16767            .expect("child rule parses");
16768
16769        // The parent's coordinate must still be present for the top-level entry.
16770        let error = parser
16771            .take_unknown_semantic_error()
16772            .expect("parent's recorded coordinate must survive the nested interpreted parse");
16773        let AntlrError::Unsupported(message) = error else {
16774            panic!("expected AntlrError::Unsupported, got {error:?}");
16775        };
16776        assert!(message.contains("pred_index=3"), "message: {message}");
16777    }
16778
16779    #[test]
16780    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
16781        let atn = predicate_after_token_atn();
16782        let mut parser = mini_parser(vec![
16783            TestToken::new(1).with_text("x"),
16784            TestToken::new(2).with_text("y"),
16785            TestToken::eof("parser-test", 2, 1, 2),
16786        ]);
16787
16788        let result = parser.parse_atn_rule_with_runtime_options(
16789            &atn,
16790            0,
16791            ParserRuntimeOptions {
16792                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16793                ..ParserRuntimeOptions::default()
16794            },
16795        );
16796
16797        assert!(
16798            result.is_err(),
16799            "the only path is predicate-guarded, so assume-false must fail the parse"
16800        );
16801    }
16802
16803    #[test]
16804    fn predicate_failure_message_keeps_semantic_recovery_path() {
16805        let atn = predicate_after_token_atn();
16806        let mut parser = mini_parser(vec![
16807            TestToken::new(1).with_text("x"),
16808            TestToken::new(2).with_text("y"),
16809            TestToken::eof("parser-test", 2, 1, 2),
16810        ]);
16811
16812        let (tree, _) = parser
16813            .parse_atn_rule_with_runtime_options(
16814                &atn,
16815                0,
16816                ParserRuntimeOptions {
16817                    predicates: &[(
16818                        0,
16819                        0,
16820                        ParserPredicate::FalseWithMessage {
16821                            message: "predicate rejected input",
16822                        },
16823                    )],
16824                    ..ParserRuntimeOptions::default()
16825                },
16826            )
16827            .expect("failure-message predicates recover through the semantic interpreter");
16828
16829        assert_eq!(parser.node(tree).text(), "xy");
16830        assert_eq!(parser.number_of_syntax_errors(), 1);
16831        assert!(
16832            parser.fast_predicate_cache.is_empty(),
16833            "failure-message predicates need the semantic interpreter's recovery outcome"
16834        );
16835    }
16836
16837    #[test]
16838    fn unknown_predicate_policy_error_names_the_coordinate() {
16839        let atn = predicate_after_token_atn();
16840        let mut parser = mini_parser(vec![
16841            TestToken::new(1).with_text("x"),
16842            TestToken::new(2).with_text("y"),
16843            TestToken::eof("parser-test", 2, 1, 2),
16844        ]);
16845
16846        let error = parser
16847            .parse_atn_rule_with_runtime_options(
16848                &atn,
16849                0,
16850                ParserRuntimeOptions {
16851                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16852                    ..ParserRuntimeOptions::default()
16853                },
16854            )
16855            .expect_err("evaluating an unknown predicate under Error policy must fail");
16856
16857        let AntlrError::Unsupported(message) = error else {
16858            panic!("expected AntlrError::Unsupported, got {error:?}");
16859        };
16860        assert!(
16861            message.contains("unsupported semantic predicate"),
16862            "message should name the failure class: {message}"
16863        );
16864        assert!(
16865            message.contains("pred_index=0"),
16866            "message should carry the coordinate: {message}"
16867        );
16868    }
16869
16870    #[test]
16871    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
16872        // A parser reused after a fail-loud parse must not carry the old
16873        // coordinates into a later parse. The fail-loud return keeps the hits
16874        // (so a generated parent can surface a recovered child's coordinate),
16875        // and the next parse's entry stashes/replaces them, so a subsequent
16876        // clean parse surfaces no stale error.
16877        let atn = predicate_after_token_atn();
16878        let mut parser = mini_parser(vec![
16879            TestToken::new(1).with_text("x"),
16880            TestToken::new(2).with_text("y"),
16881            TestToken::eof("parser-test", 2, 1, 2),
16882        ]);
16883
16884        parser
16885            .parse_atn_rule_with_runtime_options(
16886                &atn,
16887                0,
16888                ParserRuntimeOptions {
16889                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16890                    ..ParserRuntimeOptions::default()
16891                },
16892            )
16893            .expect_err("first parse fails loud under the Error policy");
16894
16895        // The failed parse kept its coordinate on the parser (so a generated
16896        // parent could surface a recovered child). A top-level reuse resets the
16897        // hits — generated parsers call `reset_unknown_semantic_hits` at their
16898        // public entry; direct interpreter-API callers do the same.
16899        parser.reset_unknown_semantic_hits();
16900        assert!(
16901            parser.take_unknown_semantic_error().is_none(),
16902            "reset must drop stale unknown-predicate coordinates before a reused parse"
16903        );
16904    }
16905
16906    #[derive(Debug, Default)]
16907    struct RecordingHooks {
16908        predicates: Vec<(usize, usize, usize, Option<String>)>,
16909        actions: Vec<(usize, String, Option<String>)>,
16910        action_trees: Vec<Option<String>>,
16911    }
16912
16913    impl SemanticHooks for RecordingHooks {
16914        fn sempred<S>(
16915            &mut self,
16916            ctx: &mut ParserSemCtx<'_, S>,
16917            rule_index: usize,
16918            pred_index: usize,
16919        ) -> Option<bool>
16920        where
16921            S: TokenSource,
16922        {
16923            self.predicates.push((
16924                ctx.input_index(),
16925                rule_index,
16926                pred_index,
16927                ctx.token_text(1)
16928                    .and_then(|token| token.text().map(str::to_owned)),
16929            ));
16930            Some(true)
16931        }
16932
16933        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
16934        where
16935            S: TokenSource,
16936        {
16937            self.actions.push((
16938                action.source_state(),
16939                ctx.action_text(),
16940                ctx.rule_name().map(str::to_owned),
16941            ));
16942            self.action_trees.push(ctx.tree().map(Node::text));
16943            true
16944        }
16945    }
16946
16947    #[derive(Debug, Default)]
16948    struct RejectingPredicateHooks {
16949        predicates: Vec<(usize, usize, usize, Option<String>)>,
16950    }
16951
16952    impl SemanticHooks for RejectingPredicateHooks {
16953        fn sempred<S>(
16954            &mut self,
16955            ctx: &mut ParserSemCtx<'_, S>,
16956            rule_index: usize,
16957            pred_index: usize,
16958        ) -> Option<bool>
16959        where
16960            S: TokenSource,
16961        {
16962            self.predicates.push((
16963                ctx.input_index(),
16964                rule_index,
16965                pred_index,
16966                ctx.token_text(1)
16967                    .and_then(|token| token.text().map(str::to_owned)),
16968            ));
16969            Some(false)
16970        }
16971    }
16972
16973    #[test]
16974    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
16975        let atn = predicate_gated_same_lookahead_atn([0, 0]);
16976        let mut parser = mini_parser_with_hooks(
16977            vec![
16978                TestToken::new(1).with_text("x"),
16979                TestToken::eof("parser-test", 1, 1, 1),
16980            ],
16981            RecordingHooks::default(),
16982        );
16983
16984        let (tree, _) = parser
16985            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16986            .expect("both alternatives share one replay-safe predicate result");
16987
16988        assert_eq!(parser.node(tree).text(), "x<EOF>");
16989        assert_eq!(
16990            parser.semantic_hooks.predicates,
16991            vec![(0, 0, 0, Some("x".to_owned()))]
16992        );
16993        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
16994    }
16995
16996    #[test]
16997    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
16998        let atn = predicate_after_token_atn();
16999        let mut parser = mini_parser_with_hooks(
17000            vec![
17001                TestToken::new(1).with_text("x"),
17002                TestToken::new(2).with_text("y"),
17003                TestToken::eof("parser-test", 2, 1, 2),
17004            ],
17005            RecordingHooks::default(),
17006        );
17007
17008        let (tree, _) = parser
17009            .parse_atn_rule_with_runtime_options(
17010                &atn,
17011                0,
17012                ParserRuntimeOptions {
17013                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17014                    ..ParserRuntimeOptions::default()
17015                },
17016            )
17017            .expect("hook supplies the missing predicate result");
17018
17019        assert_eq!(parser.node(tree).text(), "xy");
17020        assert_eq!(
17021            parser.semantic_hooks.predicates,
17022            vec![(1, 0, 0, Some("y".to_owned()))]
17023        );
17024        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
17025    }
17026
17027    #[test]
17028    fn runtime_options_default_preserves_semantic_hook_predicates() {
17029        let atn = predicate_after_token_atn();
17030        let mut parser = mini_parser_with_hooks(
17031            vec![
17032                TestToken::new(1).with_text("x"),
17033                TestToken::new(2).with_text("y"),
17034                TestToken::eof("parser-test", 2, 1, 2),
17035            ],
17036            RejectingPredicateHooks::default(),
17037        );
17038
17039        let result =
17040            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
17041
17042        assert!(
17043            result.is_err(),
17044            "default runtime options must not bypass semantic hooks for predicate ATNs"
17045        );
17046        assert_eq!(
17047            parser.semantic_hooks.predicates,
17048            vec![(1, 0, 0, Some("y".to_owned()))]
17049        );
17050        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
17051    }
17052
17053    #[test]
17054    fn semantic_hook_handles_committed_parser_action() {
17055        let atn = token_then_eof_atn();
17056        let mut parser = mini_parser_with_hooks(
17057            vec![
17058                TestToken::new(1).with_text("x"),
17059                TestToken::eof("parser-test", 1, 1, 1),
17060            ],
17061            RecordingHooks::default(),
17062        );
17063        let (tree, _) = parser
17064            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17065            .expect("rule parses before action hook is tested");
17066
17067        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17068        assert_eq!(
17069            parser.semantic_hooks.actions,
17070            vec![(42, "x".to_owned(), Some("s".to_owned()))]
17071        );
17072        assert_eq!(
17073            parser.semantic_hooks.action_trees,
17074            [Some("x<EOF>".to_owned())]
17075        );
17076    }
17077
17078    #[test]
17079    fn unhandled_committed_action_fails_loud_under_error_policy() {
17080        // An action offered to the hook that no hook handles (returns false)
17081        // must be recorded and surfaced as `AntlrError::Unsupported` under the
17082        // Error policy, so a `hook`-disposed action is not silently dropped.
17083        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17084        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17085        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
17086
17087        // DecliningHooks::action returns false (unhandled).
17088        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17089
17090        let error = parser
17091            .take_unknown_semantic_error()
17092            .expect("an unhandled committed action under Error policy must fail loud");
17093        let AntlrError::Unsupported(message) = error else {
17094            panic!("expected AntlrError::Unsupported, got {error:?}");
17095        };
17096        assert!(
17097            message.contains("unhandled semantic action") && message.contains("state=42"),
17098            "message should name the dropped action coordinate: {message}"
17099        );
17100
17101        // Under the default (assume-true) policy the same miss is not recorded.
17102        let mut lenient =
17103            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17104        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
17105        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17106        assert!(lenient.take_unknown_semantic_error().is_none());
17107    }
17108
17109    #[test]
17110    fn translated_predicate_is_unaffected_by_error_policy() {
17111        let atn = predicate_after_token_atn();
17112        let mut parser = mini_parser(vec![
17113            TestToken::new(1).with_text("x"),
17114            TestToken::new(2).with_text("y"),
17115            TestToken::eof("parser-test", 2, 1, 2),
17116        ]);
17117
17118        let (tree, _) = parser
17119            .parse_atn_rule_with_runtime_options(
17120                &atn,
17121                0,
17122                ParserRuntimeOptions {
17123                    predicates: &[(0, 0, ParserPredicate::True)],
17124                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17125                    ..ParserRuntimeOptions::default()
17126                },
17127            )
17128            .expect("a predicate covered by the table is not an unknown coordinate");
17129
17130        assert_eq!(parser.node(tree).text(), "xy");
17131    }
17132
17133    /// Hooks that decline (`None`) must fall through to the configured policy
17134    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
17135    /// legacy table path. Regression for the `unwrap_or(false)` that silently
17136    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
17137    fn hook_predicate_semantics() -> ParserSemantics {
17138        let mut ir = SemIr::new();
17139        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
17140        ParserSemantics {
17141            ir,
17142            predicates: vec![ParserSemanticPredicate {
17143                rule_index: 0,
17144                pred_index: 0,
17145                expr,
17146                failure_message: None,
17147            }],
17148            actions: Vec::new(),
17149        }
17150    }
17151
17152    #[derive(Debug, Default)]
17153    struct DecliningHooks;
17154
17155    impl SemanticHooks for DecliningHooks {}
17156
17157    #[test]
17158    fn semir_hook_none_falls_through_to_assume_true() {
17159        let atn = predicate_after_token_atn();
17160        let semantics = hook_predicate_semantics();
17161        let mut parser = mini_parser_with_hooks(
17162            vec![
17163                TestToken::new(1).with_text("x"),
17164                TestToken::new(2).with_text("y"),
17165                TestToken::eof("parser-test", 2, 1, 2),
17166            ],
17167            DecliningHooks,
17168        );
17169
17170        let (tree, _) = parser
17171            .parse_atn_rule_with_runtime_options(
17172                &atn,
17173                0,
17174                ParserRuntimeOptions {
17175                    semantics: Some(&semantics),
17176                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
17177                    ..ParserRuntimeOptions::default()
17178                },
17179            )
17180            .expect("a declined SemIR hook must pass under assume-true");
17181
17182        assert_eq!(parser.node(tree).text(), "xy");
17183    }
17184
17185    #[test]
17186    fn semir_hook_none_falls_through_to_assume_false() {
17187        let atn = predicate_after_token_atn();
17188        let semantics = hook_predicate_semantics();
17189        let mut parser = mini_parser_with_hooks(
17190            vec![
17191                TestToken::new(1).with_text("x"),
17192                TestToken::new(2).with_text("y"),
17193                TestToken::eof("parser-test", 2, 1, 2),
17194            ],
17195            DecliningHooks,
17196        );
17197
17198        let result = parser.parse_atn_rule_with_runtime_options(
17199            &atn,
17200            0,
17201            ParserRuntimeOptions {
17202                semantics: Some(&semantics),
17203                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17204                ..ParserRuntimeOptions::default()
17205            },
17206        );
17207
17208        assert!(
17209            result.is_err(),
17210            "a declined SemIR hook must fail the only guarded path under assume-false"
17211        );
17212    }
17213
17214    #[test]
17215    fn semir_hook_none_records_coordinate_under_error_policy() {
17216        let atn = predicate_after_token_atn();
17217        let semantics = hook_predicate_semantics();
17218        let mut parser = mini_parser_with_hooks(
17219            vec![
17220                TestToken::new(1).with_text("x"),
17221                TestToken::new(2).with_text("y"),
17222                TestToken::eof("parser-test", 2, 1, 2),
17223            ],
17224            DecliningHooks,
17225        );
17226
17227        let error = parser
17228            .parse_atn_rule_with_runtime_options(
17229                &atn,
17230                0,
17231                ParserRuntimeOptions {
17232                    semantics: Some(&semantics),
17233                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17234                    ..ParserRuntimeOptions::default()
17235                },
17236            )
17237            .expect_err("a declined SemIR hook under Error policy must fail the parse");
17238
17239        let AntlrError::Unsupported(message) = error else {
17240            panic!("expected AntlrError::Unsupported, got {error:?}");
17241        };
17242        assert!(
17243            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
17244            "message should name the unresolved coordinate: {message}"
17245        );
17246    }
17247
17248    #[test]
17249    fn generated_direct_predicate_honors_installed_policy() {
17250        // The generated recursive-descent path calls
17251        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
17252        // going through `ParserRuntimeOptions`, so the policy must be installed
17253        // via `set_unknown_predicate_policy` (as the generated constructor now
17254        // does). A declining hook must then honor it rather than the default.
17255        let semantics = hook_predicate_semantics();
17256        let context = ParserRuleContext::new(0, -1);
17257
17258        let mut assume_true =
17259            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17260        assert!(
17261            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
17262                &semantics, 0, 0, &context, 0
17263            ),
17264            "default AssumeTrue accepts a declined hook"
17265        );
17266        assert!(assume_true.take_unknown_semantic_error().is_none());
17267
17268        let mut error_policy =
17269            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17270        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17271        assert!(
17272            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
17273                &semantics, 0, 0, &context, 0
17274            ),
17275            "Error policy rejects a declined hook on the generated-direct path"
17276        );
17277        let error = error_policy
17278            .take_unknown_semantic_error()
17279            .expect("Error policy records the unresolved coordinate for the generated path");
17280        let AntlrError::Unsupported(message) = error else {
17281            panic!("expected AntlrError::Unsupported, got {error:?}");
17282        };
17283        assert!(message.contains("pred_index=0"), "message: {message}");
17284    }
17285
17286    #[test]
17287    fn parser_rule_start_skips_leading_hidden_tokens() {
17288        let atn = token_then_eof_atn();
17289        let mut parser = mini_parser(vec![
17290            TestToken::new(99)
17291                .with_text(" ")
17292                .with_channel(HIDDEN_CHANNEL),
17293            TestToken::new(1).with_text("x"),
17294            TestToken::eof("parser-test", 2, 1, 2),
17295        ]);
17296
17297        let tree = parser
17298            .parse_atn_rule(&atn, 0)
17299            .expect("artificial parser rule should parse");
17300        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
17301            panic!("rule node should be present");
17302        };
17303        assert_eq!(
17304            rule.start()
17305                .expect("rule should have a start token")
17306                .token_type(),
17307            1
17308        );
17309    }
17310
17311    #[test]
17312    fn parser_action_after_eof_stops_at_eof_token() {
17313        let atn = eof_then_action_atn();
17314        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
17315
17316        let (_, actions) = parser
17317            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17318            .expect("EOF action rule should parse");
17319
17320        assert_eq!(actions.len(), 1);
17321        assert_eq!(actions[0].stop_index(), Some(0));
17322        assert_eq!(
17323            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
17324            ""
17325        );
17326    }
17327
17328    #[test]
17329    fn after_action_stop_uses_rule_context_stop_not_cursor() {
17330        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
17331        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
17332        // but the rule did not consume it. The @after stop must follow the rule
17333        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
17334        let mut id = TestToken::new(1).with_text("x");
17335        id.set_token_index(0);
17336        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
17337        eof.set_token_index(1);
17338        let mut parser = mini_parser(vec![id.clone(), eof]);
17339        // Advance the cursor onto EOF, as it would be after `a` matched ID.
17340        parser.consume();
17341        assert_eq!(parser.la(1), TOKEN_EOF);
17342
17343        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
17344        // exactly what finish_rule(consumed_eof = false) records.
17345        let mut ctx = ParserRuleContext::new(0, 0);
17346        parser.set_context_stop(
17347            &mut ctx,
17348            parser.token_id_at(0).expect("ID token should be buffered"),
17349        );
17350        let tree = parser.rule_node(ctx);
17351
17352        let current_index = parser.input.index();
17353        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
17354        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
17355        // ...but the tree-aware helper follows the rule context stop (ID).
17356        assert_eq!(
17357            parser.after_action_stop_index_for_tree(tree, current_index),
17358            Some(0)
17359        );
17360    }
17361
17362    #[test]
17363    fn after_action_start_uses_rule_context_start_not_cursor() {
17364        // A rule that begins after leading hidden-channel tokens: the rule context
17365        // start (set by `enter_rule`) is the first visible token, not the raw cursor
17366        // that may still point at the hidden prefix. The @after start must follow
17367        // the context start so `$start`/`$text` excludes the hidden prefix.
17368        let mut parser = mini_parser(vec![
17369            TestToken::new(9)
17370                .with_text(" ")
17371                .with_channel(HIDDEN_CHANNEL),
17372            TestToken::new(9)
17373                .with_text(" ")
17374                .with_channel(HIDDEN_CHANNEL),
17375            TestToken::new(1).with_text("x"),
17376            TestToken::eof("parser-test", 3, 1, 3),
17377        ]);
17378
17379        let mut ctx = ParserRuleContext::new(0, 0);
17380        parser.set_context_start(
17381            &mut ctx,
17382            parser.token_id_at(2).expect("ID token should be buffered"),
17383        );
17384        let tree = parser.rule_node(ctx);
17385
17386        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
17387        // ...but the tree-aware helper follows the rule context start (index 2).
17388        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
17389
17390        // With no rule start recorded, it falls back to the provided index.
17391        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
17392        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
17393    }
17394
17395    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
17396        FastRecognizeOutcome {
17397            index,
17398            consumed_eof,
17399            diagnostics: DiagnosticSeqId::EMPTY,
17400            deferred_nodes: FastDeferredNodeId::EMPTY,
17401            nodes: NodeSeqId(marker),
17402        }
17403    }
17404
17405    #[test]
17406    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
17407        let mut outcomes = vec![
17408            clean_fast_outcome(4, false, 0),
17409            clean_fast_outcome(2, false, 1),
17410            clean_fast_outcome(4, false, 2),
17411            clean_fast_outcome(4, true, 3),
17412            clean_fast_outcome(2, false, 4),
17413        ];
17414        let mut scratch = FastOutcomeDedupScratch::default();
17415
17416        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17417
17418        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
17419        assert_eq!(
17420            outcomes
17421                .iter()
17422                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
17423                .collect::<Vec<_>>(),
17424            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
17425        );
17426        assert!(scratch.dense_words.is_empty());
17427        assert!(scratch.sparse_keys.is_empty());
17428    }
17429
17430    #[test]
17431    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
17432        let mut scratch = FastOutcomeDedupScratch::default();
17433        let mut outcomes = (100..109)
17434            .flat_map(|index| {
17435                [
17436                    clean_fast_outcome(
17437                        index,
17438                        false,
17439                        u32::try_from(index).expect("test index fits in u32"),
17440                    ),
17441                    clean_fast_outcome(index, false, u32::MAX),
17442                ]
17443            })
17444            .collect();
17445
17446        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17447
17448        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
17449        assert_eq!(outcomes.len(), 9);
17450        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
17451        let dense_capacity = scratch.dense_words.capacity();
17452
17453        let mut reused = (1_000..1_009)
17454            .map(|index| {
17455                clean_fast_outcome(
17456                    index,
17457                    false,
17458                    u32::try_from(index).expect("test index fits in u32"),
17459                )
17460            })
17461            .collect();
17462        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
17463
17464        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
17465        assert_eq!(reused.len(), 9);
17466        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
17467    }
17468
17469    #[test]
17470    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
17471        let mut scratch = FastOutcomeDedupScratch::default();
17472        let sparse_indexes = [
17473            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
17474        ];
17475        let mut outcomes = sparse_indexes
17476            .into_iter()
17477            .chain([400_000])
17478            .enumerate()
17479            .map(|(marker, index)| {
17480                clean_fast_outcome(
17481                    index,
17482                    false,
17483                    u32::try_from(marker).expect("test marker fits in u32"),
17484                )
17485            })
17486            .collect();
17487
17488        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17489
17490        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
17491        assert_eq!(outcomes.len(), sparse_indexes.len());
17492        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
17493        let sparse_capacity = scratch.sparse_keys.capacity();
17494
17495        let mut reused = sparse_indexes
17496            .into_iter()
17497            .map(|index| {
17498                clean_fast_outcome(
17499                    index,
17500                    false,
17501                    u32::try_from(index).expect("test index fits in u32"),
17502                )
17503            })
17504            .collect();
17505        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
17506
17507        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
17508        assert_eq!(reused.len(), sparse_indexes.len());
17509        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
17510    }
17511
17512    #[test]
17513    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
17514        let mut scratch = FastOutcomeDedupScratch::default();
17515        scratch
17516            .sparse_keys
17517            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
17518        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
17519        let mut outcomes = (0..9)
17520            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
17521            .collect();
17522
17523        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17524
17525        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
17526        assert!(scratch.sparse_keys.is_empty());
17527        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
17528    }
17529
17530    #[test]
17531    fn fast_outcome_selection_respects_sll_tie_order() {
17532        let mut arena = RecognitionArena::default();
17533        let first = FastRecognizeOutcome {
17534            index: 1,
17535            consumed_eof: false,
17536            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17537                line: 1,
17538                column: 0,
17539                message: "mismatched input 'x'".to_owned(),
17540            }]),
17541            deferred_nodes: FastDeferredNodeId::EMPTY,
17542            nodes: NodeSeqId::EMPTY,
17543        };
17544        let second = FastRecognizeOutcome {
17545            index: first.index,
17546            consumed_eof: first.consumed_eof,
17547            diagnostics: DiagnosticSeqId::EMPTY,
17548            deferred_nodes: FastDeferredNodeId::EMPTY,
17549            nodes: NodeSeqId::EMPTY,
17550        };
17551
17552        let selected = select_best_fast_outcome(
17553            [first, second].into_iter(),
17554            PredictionMode::Sll,
17555            None,
17556            |_| panic!("caller-follow token probe should not run"),
17557            &arena,
17558        )
17559        .expect("one outcome should be selected");
17560        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
17561        let eof_second = FastRecognizeOutcome {
17562            index: second.index,
17563            consumed_eof: true,
17564            diagnostics: DiagnosticSeqId::EMPTY,
17565            deferred_nodes: FastDeferredNodeId::EMPTY,
17566            nodes: NodeSeqId::EMPTY,
17567        };
17568        let selected = select_best_fast_outcome(
17569            [first, eof_second].into_iter(),
17570            PredictionMode::Sll,
17571            None,
17572            |_| panic!("caller-follow token probe should not run"),
17573            &arena,
17574        )
17575        .expect("one outcome should be selected");
17576        assert!(!selected.consumed_eof);
17577        let selected = select_best_fast_outcome(
17578            [first, second].into_iter(),
17579            PredictionMode::Ll,
17580            None,
17581            |_| panic!("caller-follow token probe should not run"),
17582            &arena,
17583        )
17584        .expect("one outcome should be selected");
17585        assert!(selected.diagnostics.is_empty());
17586    }
17587
17588    #[test]
17589    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
17590        let mut arena = RecognitionArena::default();
17591        let first = FastRecognizeOutcome {
17592            index: 3,
17593            consumed_eof: false,
17594            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17595                line: 1,
17596                column: 0,
17597                message: "mismatched input 'x' expecting 'a'".to_owned(),
17598            }]),
17599            deferred_nodes: FastDeferredNodeId::EMPTY,
17600            nodes: NodeSeqId::EMPTY,
17601        };
17602        let same_rank = FastRecognizeOutcome {
17603            index: first.index,
17604            consumed_eof: first.consumed_eof,
17605            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17606                line: 1,
17607                column: 0,
17608                message: "mismatched input 'x' expecting 'b'".to_owned(),
17609            }]),
17610            deferred_nodes: FastDeferredNodeId::EMPTY,
17611            nodes: NodeSeqId::EMPTY,
17612        };
17613        let better_rank = FastRecognizeOutcome {
17614            index: first.index,
17615            consumed_eof: first.consumed_eof,
17616            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17617                line: 1,
17618                column: 0,
17619                message: "missing 'a' at 'x'".to_owned(),
17620            }]),
17621            deferred_nodes: FastDeferredNodeId::EMPTY,
17622            nodes: NodeSeqId::EMPTY,
17623        };
17624        let mut outcomes = vec![first, same_rank, better_rank];
17625
17626        dedupe_fast_outcomes(&mut outcomes, &arena);
17627
17628        assert_eq!(outcomes.len(), 2);
17629        assert_eq!(
17630            arena
17631                .diagnostics(outcomes[0].diagnostics)
17632                .next()
17633                .expect("first diagnostic")
17634                .message,
17635            "mismatched input 'x' expecting 'a'"
17636        );
17637        assert_eq!(
17638            arena
17639                .diagnostics(outcomes[1].diagnostics)
17640                .next()
17641                .expect("second diagnostic")
17642                .message,
17643            "missing 'a' at 'x'"
17644        );
17645    }
17646
17647    #[test]
17648    fn fast_outcome_selection_prefers_generated_caller_follow() {
17649        let arena = RecognitionArena::default();
17650        let earlier = FastRecognizeOutcome {
17651            index: 7,
17652            consumed_eof: false,
17653            diagnostics: DiagnosticSeqId::EMPTY,
17654            deferred_nodes: FastDeferredNodeId::EMPTY,
17655            nodes: NodeSeqId::EMPTY,
17656        };
17657        let later = FastRecognizeOutcome {
17658            index: 8,
17659            consumed_eof: false,
17660            diagnostics: DiagnosticSeqId::EMPTY,
17661            deferred_nodes: FastDeferredNodeId::EMPTY,
17662            nodes: NodeSeqId::EMPTY,
17663        };
17664        let mut follow = TokenBitSet::default();
17665        follow.insert(5);
17666
17667        let selected = select_best_fast_outcome(
17668            [later, earlier].into_iter(),
17669            PredictionMode::Ll,
17670            Some(&follow),
17671            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
17672            &arena,
17673        )
17674        .expect("one outcome should be selected");
17675        assert_eq!(selected.index, 7);
17676
17677        let selected = select_best_fast_outcome(
17678            [later, earlier].into_iter(),
17679            PredictionMode::Ll,
17680            Some(&follow),
17681            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
17682            &arena,
17683        )
17684        .expect("one outcome should be selected");
17685        assert_eq!(selected.index, 8);
17686
17687        let indented_next_statement = FastRecognizeOutcome {
17688            index: 9,
17689            consumed_eof: false,
17690            diagnostics: DiagnosticSeqId::EMPTY,
17691            deferred_nodes: FastDeferredNodeId::EMPTY,
17692            nodes: NodeSeqId::EMPTY,
17693        };
17694        let selected = select_best_fast_outcome(
17695            [indented_next_statement, earlier].into_iter(),
17696            PredictionMode::Ll,
17697            Some(&follow),
17698            |index| {
17699                let is_boundary = index == 7;
17700                let is_boundary_gap = matches!(index, 7 | 8);
17701                (
17702                    if index == 7 { 5 } else { TOKEN_EOF },
17703                    is_boundary,
17704                    is_boundary_gap,
17705                )
17706            },
17707            &arena,
17708        )
17709        .expect("one outcome should be selected");
17710        assert_eq!(selected.index, 7);
17711
17712        let continuation = FastRecognizeOutcome {
17713            index: 10,
17714            consumed_eof: false,
17715            diagnostics: DiagnosticSeqId::EMPTY,
17716            deferred_nodes: FastDeferredNodeId::EMPTY,
17717            nodes: NodeSeqId::EMPTY,
17718        };
17719        let selected = select_best_fast_outcome(
17720            [continuation, earlier].into_iter(),
17721            PredictionMode::Ll,
17722            Some(&follow),
17723            |index| {
17724                let is_boundary = matches!(index, 7 | 9);
17725                (
17726                    if index == 7 { 5 } else { TOKEN_EOF },
17727                    is_boundary,
17728                    is_boundary,
17729                )
17730            },
17731            &arena,
17732        )
17733        .expect("one outcome should be selected");
17734        assert_eq!(selected.index, 10);
17735
17736        let selected = select_best_fast_outcome(
17737            [earlier, later].into_iter(),
17738            PredictionMode::Sll,
17739            Some(&follow),
17740            |_| panic!("caller-follow token probe should not run in SLL mode"),
17741            &arena,
17742        )
17743        .expect("one outcome should be selected");
17744        assert_eq!(selected.index, 8);
17745    }
17746
17747    #[test]
17748    fn caller_follow_boundary_text_requires_separator_shape() {
17749        assert!(is_caller_follow_boundary_text(";"));
17750        assert!(is_caller_follow_boundary_text("\n"));
17751        assert!(is_caller_follow_boundary_text("\r\n  "));
17752        assert!(is_caller_follow_boundary_text(";\n"));
17753        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
17754        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
17755        assert!(!is_caller_follow_boundary_text("identifier"));
17756        assert!(is_caller_follow_boundary_gap_text(" \t "));
17757        assert!(is_caller_follow_boundary_gap_text("\n  "));
17758        assert!(is_caller_follow_boundary_gap_text(";\t"));
17759        assert!(!is_caller_follow_boundary_gap_text(
17760            "\"\"\"line1\nline2\"\"\""
17761        ));
17762        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
17763    }
17764
17765    #[test]
17766    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
17767        let mut parser = mini_parser(vec![
17768            TestToken::new(5).with_text("\n"),
17769            TestToken::new(6)
17770                .with_text("// comment\n")
17771                .with_channel(HIDDEN_CHANNEL),
17772            TestToken::new(1).with_text("x"),
17773            TestToken::eof("parser-test", 1, 2, 0),
17774        ]);
17775
17776        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17777        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
17778        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
17779    }
17780
17781    #[test]
17782    fn caller_follow_token_info_uses_stream_visible_channel() {
17783        let source = Source {
17784            tokens: vec![
17785                TestToken::new(5).with_text("\n").with_channel(2),
17786                TestToken::new(1).with_text("x").with_channel(2),
17787                TestToken::new(6)
17788                    .with_text("// comment\n")
17789                    .with_channel(HIDDEN_CHANNEL),
17790                TestToken::eof("parser-test", 1, 2, 0),
17791            ],
17792            index: 0,
17793        };
17794        let data = RecognizerData::new(
17795            "Mini.g4",
17796            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17797        );
17798        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
17799
17800        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17801        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
17802        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
17803    }
17804
17805    #[test]
17806    fn reset_per_parse_caches_clears_state_expected_token_cache() {
17807        let atn = token_then_eof_atn();
17808        let mut parser = mini_parser(Vec::new());
17809
17810        let _ = parser.cached_state_expected_token_set(&atn, 0);
17811        assert!(!parser.state_expected_token_cache.is_empty());
17812
17813        parser.reset_per_parse_caches();
17814        assert!(parser.state_expected_token_cache.is_empty());
17815    }
17816
17817    #[test]
17818    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
17819        let cyclic = epsilon_cycle_atn();
17820        let acyclic = token_then_eof_atn();
17821        let mut parser = mini_parser(Vec::new());
17822
17823        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17824        assert_eq!(
17825            parser.empty_cycle_cache_atn,
17826            Some(SharedAtnCacheKey::for_atn(&cyclic))
17827        );
17828        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17829
17830        parser.reset_per_parse_caches();
17831        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17832        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17833
17834        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
17835        assert_eq!(
17836            parser.empty_cycle_cache_atn,
17837            Some(SharedAtnCacheKey::for_atn(&acyclic))
17838        );
17839        assert_eq!(parser.empty_cycle_cache[1], Some(false));
17840    }
17841
17842    #[test]
17843    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
17844        let source = Source {
17845            tokens: vec![
17846                TestToken::new(1).with_text("x"),
17847                TestToken::eof("parser-test", 1, 1, 1),
17848            ],
17849            index: 0,
17850        };
17851        let data = RecognizerData::new(
17852            "Mini.g4",
17853            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17854        );
17855        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17856        let expected = ExpectedTokens {
17857            index: Some(0),
17858            symbols: BTreeSet::new(),
17859            no_viable: None,
17860        };
17861
17862        let (_, message) = parser.expected_error_message(0, 0, &expected);
17863
17864        assert_eq!(message, "mismatched input 'x'");
17865    }
17866
17867    #[test]
17868    fn eof_rule_stop_index_points_at_eof_token() {
17869        let source = Source {
17870            tokens: vec![
17871                TestToken::new(1).with_text("x"),
17872                TestToken::eof("parser-test", 1, 1, 1),
17873            ],
17874            index: 0,
17875        };
17876        let data = RecognizerData::new(
17877            "Mini.g4",
17878            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17879        );
17880        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17881
17882        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
17883        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
17884    }
17885
17886    #[test]
17887    fn generated_parser_action_uses_current_rule_stop_boundary() {
17888        let mut parser = mini_parser(vec![
17889            TestToken::new(1).with_text("x"),
17890            TestToken::eof("parser-test", 1, 1, 1),
17891        ]);
17892
17893        parser.match_token(1).expect("token should match");
17894        let action = parser.parser_action_at_current(7, 0, 0, false);
17895        assert_eq!(action.source_state(), 7);
17896        assert_eq!(action.rule_index(), 0);
17897        assert_eq!(action.start_index(), 0);
17898        assert_eq!(action.stop_index(), Some(0));
17899
17900        parser.match_eof().expect("EOF should match");
17901        let action = parser.parser_action_at_current(8, 0, 0, true);
17902        assert_eq!(action.stop_index(), Some(1));
17903    }
17904
17905    #[test]
17906    fn folds_left_recursive_boundary_into_rule_node() {
17907        let mut arena = RecognitionArena::default();
17908        let first = arena.push_node(ArenaRecognizedNode::Token {
17909            token: TokenId::try_from(0).expect("test token ID"),
17910        });
17911        let boundary = arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
17912            rule_index: 1,
17913            alt_number: 3,
17914        });
17915        let second = arena.push_node(ArenaRecognizedNode::Token {
17916            token: TokenId::try_from(1).expect("test token ID"),
17917        });
17918        let mut nodes = NodeSeqId::EMPTY;
17919        for node in [first, boundary, second].into_iter().rev() {
17920            nodes = arena.prepend(nodes, node);
17921        }
17922
17923        let folded = arena.fold_left_recursive_boundaries(nodes);
17924        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
17925
17926        assert_eq!(folded_nodes.len(), 2);
17927        let ArenaRecognizedNode::Rule {
17928            rule_index,
17929            invoking_state,
17930            alt_number,
17931            start_index,
17932            stop_index,
17933            children,
17934            ..
17935        } = arena.node(folded_nodes[0])
17936        else {
17937            panic!("first folded node should be a rule");
17938        };
17939        // The folded rule node's scalar shape (rule/invoking-state/alt/start/stop) is one snapshot;
17940        // child resolution and the sibling identity below stay explicit — a node Debug prints the
17941        // children handle, not the resolved sequence they assert on.
17942        insta::assert_debug_snapshot!(
17943            "folds_left_recursive_boundary_into_rule_node",
17944            (
17945                rule_index,
17946                invoking_state,
17947                alt_number,
17948                start_index,
17949                stop_index
17950            )
17951        );
17952        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
17953        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
17954
17955        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
17956        assert_eq!(
17957            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17958            (4, 3, 1)
17959        );
17960        assert_eq!(
17961            (stats.total_links, stats.live_links, stats.dead_links),
17962            (9, 3, 6)
17963        );
17964    }
17965
17966    #[test]
17967    fn recognition_arena_reports_live_dead_and_retained_capacity() {
17968        let mut arena = RecognitionArena::default();
17969        let token = arena.push_node(ArenaRecognizedNode::Token {
17970            token: TokenId::try_from(0).expect("test token ID"),
17971        });
17972        let extra = arena.push_extra(RecognitionExtra::MissingToken {
17973            token_type: 2,
17974            at_index: 1,
17975            text: "<missing X>".to_owned(),
17976        });
17977        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
17978        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
17979            token: TokenId::try_from(1).expect("test token ID"),
17980        });
17981        let mut live = NodeSeqId::EMPTY;
17982        live = arena.prepend(live, missing);
17983        live = arena.prepend(live, token);
17984        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
17985        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17986            line: 1,
17987            column: 0,
17988            message: "missing X".to_owned(),
17989        }]);
17990        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17991            line: 1,
17992            column: 1,
17993            message: "discarded".to_owned(),
17994        }]);
17995        let deferred_children = arena.deferred_fragment(live);
17996        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
17997            rule_index: 0,
17998            invoking_state: -1,
17999            start_index: 0,
18000            stop_index: Some(1),
18001            deferred_children,
18002            children: NodeSeqId::EMPTY,
18003        });
18004
18005        let stats = arena.stats(live, live_diagnostics);
18006
18007        assert_eq!(
18008            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18009            (3, 2, 1)
18010        );
18011        assert_eq!(
18012            (stats.total_links, stats.live_links, stats.dead_links),
18013            (5, 3, 2)
18014        );
18015        assert_eq!(
18016            (stats.total_extras, stats.live_extras, stats.dead_extras),
18017            (3, 2, 1)
18018        );
18019        assert!(size_of::<SeqLink>() <= 8);
18020        assert!(size_of::<DiagnosticLink>() <= 8);
18021        assert!(size_of::<FastDeferredNode>() <= 12);
18022        assert!(size_of::<FastDeferredRule>() <= 28);
18023        assert!(size_of::<FastRecognizeOutcome>() <= 24);
18024        let capacities = (
18025            stats.node_capacity,
18026            stats.link_capacity,
18027            stats.extra_capacity,
18028        );
18029        let deferred_capacities = (
18030            arena.deferred_nodes.capacity(),
18031            arena.deferred_rules.capacity(),
18032        );
18033
18034        arena.reset();
18035        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
18036        assert_eq!(
18037            (reset.total_nodes, reset.total_links, reset.total_extras),
18038            (0, 0, 0)
18039        );
18040        assert_eq!(
18041            (
18042                reset.node_capacity,
18043                reset.link_capacity,
18044                reset.extra_capacity,
18045            ),
18046            capacities
18047        );
18048        assert!(arena.deferred_nodes.is_empty());
18049        assert!(arena.deferred_rules.is_empty());
18050        assert_eq!(
18051            (
18052                arena.deferred_nodes.capacity(),
18053                arena.deferred_rules.capacity(),
18054            ),
18055            deferred_capacities
18056        );
18057    }
18058
18059    #[test]
18060    fn parser_computes_recognition_arena_stats_on_demand() {
18061        let mut parser = mini_parser(Vec::new());
18062        let live = parser
18063            .recognition_arena
18064            .push_node(ArenaRecognizedNode::Token {
18065                token: TokenId::try_from(0).expect("test token ID"),
18066            });
18067        let discarded = parser
18068            .recognition_arena
18069            .push_node(ArenaRecognizedNode::ErrorToken {
18070                token: TokenId::try_from(1).expect("test token ID"),
18071            });
18072        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
18073        let _discarded_root = parser
18074            .recognition_arena
18075            .prepend(NodeSeqId::EMPTY, discarded);
18076        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
18077
18078        let stats = parser.recognition_arena_stats();
18079
18080        assert_eq!(
18081            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18082            (2, 1, 1)
18083        );
18084        assert_eq!(
18085            (stats.total_links, stats.live_links, stats.dead_links),
18086            (2, 1, 1)
18087        );
18088    }
18089
18090    #[test]
18091    fn recognition_arena_drops_capacity_above_retention_limit() {
18092        let mut storage = Vec::<u8>::with_capacity(4);
18093        storage.extend([1, 2, 3]);
18094
18095        reset_arena_vec(&mut storage, 3);
18096
18097        assert!(storage.is_empty());
18098        assert_eq!(storage.capacity(), 0);
18099    }
18100
18101    #[test]
18102    fn recognition_arena_concatenates_diagnostics_in_source_order() {
18103        let mut arena = RecognitionArena::default();
18104        let prefix = arena.diagnostic_sequence([
18105            ParserDiagnostic {
18106                line: 1,
18107                column: 0,
18108                message: "first".to_owned(),
18109            },
18110            ParserDiagnostic {
18111                line: 1,
18112                column: 1,
18113                message: "second".to_owned(),
18114            },
18115        ]);
18116        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
18117            line: 1,
18118            column: 2,
18119            message: "third".to_owned(),
18120        }]);
18121        let extras_before = arena.extras.len();
18122
18123        let combined = arena.concat_diagnostics(prefix, suffix);
18124        let messages = arena
18125            .diagnostics(combined)
18126            .map(|diagnostic| diagnostic.message.as_str())
18127            .collect::<Vec<_>>();
18128
18129        assert_eq!(messages, ["first", "second", "third"]);
18130        assert_eq!(arena.extras.len(), extras_before);
18131    }
18132
18133    #[test]
18134    fn outcome_ties_keep_later_non_recursive_alternative() {
18135        let arena = RecognitionArena::default();
18136        let first = RecognizeOutcome {
18137            index: 1,
18138            consumed_eof: false,
18139            alt_number: 0,
18140            member_values: BTreeMap::new(),
18141            return_values: BTreeMap::new(),
18142            diagnostics: DiagnosticSeqId::EMPTY,
18143            decisions: Vec::new(),
18144            actions: vec![ParserAction::new(1, 0, 0, None)],
18145            nodes: NodeSeqId::EMPTY,
18146        };
18147        let second = RecognizeOutcome {
18148            actions: vec![ParserAction::new(2, 0, 0, None)],
18149            ..first.clone()
18150        };
18151
18152        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18153            .expect("one outcome should be selected");
18154        assert_eq!(selected.actions[0].source_state(), 2);
18155    }
18156
18157    #[test]
18158    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
18159        let arena = RecognitionArena::default();
18160        let first = RecognizeOutcome {
18161            index: 1,
18162            consumed_eof: false,
18163            alt_number: 0,
18164            member_values: BTreeMap::new(),
18165            return_values: BTreeMap::new(),
18166            diagnostics: DiagnosticSeqId::EMPTY,
18167            decisions: Vec::new(),
18168            actions: vec![ParserAction::new(1, 0, 0, None)],
18169            nodes: NodeSeqId::EMPTY,
18170        };
18171        let second = RecognizeOutcome {
18172            actions: vec![
18173                ParserAction::new(2, 0, 0, None),
18174                ParserAction::new(3, 0, 0, None),
18175            ],
18176            ..first.clone()
18177        };
18178
18179        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
18180            .expect("one outcome should be selected");
18181        assert_eq!(selected.actions.len(), 2);
18182    }
18183
18184    #[test]
18185    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
18186        let arena = RecognitionArena::default();
18187        let first = RecognizeOutcome {
18188            index: 7,
18189            consumed_eof: false,
18190            alt_number: 0,
18191            member_values: BTreeMap::new(),
18192            return_values: BTreeMap::new(),
18193            diagnostics: DiagnosticSeqId::EMPTY,
18194            decisions: vec![1, 0],
18195            actions: vec![
18196                ParserAction::new(23, 2, 2, Some(4)),
18197                ParserAction::new(23, 2, 0, Some(6)),
18198            ],
18199            nodes: NodeSeqId::EMPTY,
18200        };
18201        let second = RecognizeOutcome {
18202            decisions: vec![0, 1],
18203            actions: vec![
18204                ParserAction::new(23, 2, 2, Some(6)),
18205                ParserAction::new(23, 2, 0, Some(6)),
18206            ],
18207            ..first.clone()
18208        };
18209
18210        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18211            .expect("one outcome should be selected");
18212        assert_eq!(selected.actions[0].stop_index(), Some(6));
18213    }
18214
18215    #[test]
18216    fn outcome_ties_keep_first_recursive_tree_shape() {
18217        let mut arena = RecognitionArena::default();
18218        let token = arena.push_node(ArenaRecognizedNode::Token {
18219            token: TokenId::try_from(0).expect("test token ID"),
18220        });
18221        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
18222        let inner = arena.push_node(ArenaRecognizedNode::Rule {
18223            rule_index: 1,
18224            invoking_state: -1,
18225            alt_number: 0,
18226            start_index: 0,
18227            stop_index: Some(0),
18228            return_values: None,
18229            children: token_children,
18230        });
18231        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
18232        let outer = arena.push_node(ArenaRecognizedNode::Rule {
18233            rule_index: 1,
18234            invoking_state: -1,
18235            alt_number: 0,
18236            start_index: 0,
18237            stop_index: Some(0),
18238            return_values: None,
18239            children: inner_children,
18240        });
18241        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
18242        let first = RecognizeOutcome {
18243            index: 1,
18244            consumed_eof: false,
18245            alt_number: 0,
18246            member_values: BTreeMap::new(),
18247            return_values: BTreeMap::new(),
18248            diagnostics: DiagnosticSeqId::EMPTY,
18249            decisions: Vec::new(),
18250            actions: vec![ParserAction::new(1, 0, 0, None)],
18251            nodes: recursive_nodes,
18252        };
18253        let second = RecognizeOutcome {
18254            index: 1,
18255            consumed_eof: false,
18256            alt_number: 0,
18257            member_values: BTreeMap::new(),
18258            return_values: BTreeMap::new(),
18259            diagnostics: DiagnosticSeqId::EMPTY,
18260            decisions: Vec::new(),
18261            actions: vec![ParserAction::new(2, 0, 0, None)],
18262            nodes: recursive_nodes,
18263        };
18264
18265        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18266            .expect("one outcome should be selected");
18267        assert_eq!(selected.actions[0].source_state(), 1);
18268    }
18269
18270    #[test]
18271    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
18272        let mut arena = RecognitionArena::default();
18273        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18274            line: 1,
18275            column: 3,
18276            message: "missing 'Y' at '<EOF>'".to_owned(),
18277        }]);
18278        let first_alt = RecognizeOutcome {
18279            index: 2,
18280            consumed_eof: true,
18281            alt_number: 0,
18282            member_values: BTreeMap::new(),
18283            return_values: BTreeMap::new(),
18284            diagnostics: recovered_diagnostics,
18285            decisions: vec![0],
18286            actions: vec![ParserAction::new(1, 0, 0, None)],
18287            nodes: NodeSeqId::EMPTY,
18288        };
18289        let second_alt = RecognizeOutcome {
18290            diagnostics: DiagnosticSeqId::EMPTY,
18291            decisions: vec![1],
18292            actions: vec![ParserAction::new(2, 0, 0, None)],
18293            ..first_alt.clone()
18294        };
18295
18296        let selected = select_best_outcome(
18297            [second_alt, first_alt].into_iter(),
18298            PredictionMode::Sll,
18299            &arena,
18300        )
18301        .expect("one outcome should be selected");
18302        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
18303        assert_eq!(selected.decisions, [0]);
18304    }
18305}