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.max(1));
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).max(1));
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).max(1));
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                    let next = self.next_visible_after_token(child_stop);
7099                    cursor = match (cursor, next) {
7100                        (None, _) | (_, None) => None,
7101                        (Some(current), Some(next)) => Some(current.max(next)),
7102                    };
7103                }
7104            } else {
7105                let child =
7106                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7107                self.tree.add_child(context, child);
7108            }
7109            children = link.tail;
7110        }
7111        if let Some(stop) = stop_index {
7112            self.add_visible_terminals_through(context, cursor, stop)?;
7113        }
7114        Ok(())
7115    }
7116
7117    fn add_visible_terminals_before(
7118        &mut self,
7119        context: &mut ParserRuleContext,
7120        cursor: &mut Option<usize>,
7121        before: usize,
7122    ) -> Result<(), AntlrError> {
7123        let Some(stop) = before.checked_sub(1) else {
7124            return Ok(());
7125        };
7126        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
7127        *cursor = next;
7128        Ok(())
7129    }
7130
7131    fn add_visible_terminals_through(
7132        &mut self,
7133        context: &mut ParserRuleContext,
7134        mut cursor: Option<usize>,
7135        stop: usize,
7136    ) -> Result<Option<usize>, AntlrError> {
7137        while let Some(index) = cursor {
7138            if index > stop {
7139                return Ok(Some(index));
7140            }
7141            let token = self
7142                .input
7143                .get_id(index)
7144                .ok_or_else(|| AntlrError::ParserError {
7145                    line: 0,
7146                    column: 0,
7147                    message: format!("missing token at index {index}"),
7148                })?;
7149            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
7150            let child = self.terminal_tree(token);
7151            self.tree.add_child(context, child);
7152            if is_eof {
7153                return Ok(None);
7154            }
7155            cursor = self.next_visible_after_token(index);
7156        }
7157        Ok(None)
7158    }
7159
7160    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
7161        let next = self.input.next_visible_after(index);
7162        (next != index).then_some(next)
7163    }
7164
7165    /// Parses a generated rule and returns semantic actions reached on the
7166    /// selected ATN path.
7167    ///
7168    /// This slower path preserves action ordering and token intervals for
7169    /// generated code that replays target-specific action templates after the
7170    /// recognizer has chosen one viable parse path.
7171    pub fn parse_atn_rule_with_actions(
7172        &mut self,
7173        atn: &Atn,
7174        rule_index: usize,
7175    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7176        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
7177    }
7178
7179    /// Parses a generated rule and emits ATN actions plus selected rule-init
7180    /// actions reached on the chosen path.
7181    ///
7182    /// Generated parsers use this when a grammar contains rule-level `@init`
7183    /// templates that must run for nested rule invocations. The runtime keeps
7184    /// the action list path-sensitive, so init templates are replayed only for
7185    /// rules that were actually entered by the selected parse.
7186    pub fn parse_atn_rule_with_action_inits(
7187        &mut self,
7188        atn: &Atn,
7189        rule_index: usize,
7190        init_action_rules: &[usize],
7191    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7192        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7193    }
7194
7195    /// Parses a generated rule with optional semantic-action replay features.
7196    ///
7197    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7198    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7199    /// unchanged for grammars that do not request that target template.
7200    pub fn parse_atn_rule_with_action_options(
7201        &mut self,
7202        atn: &Atn,
7203        rule_index: usize,
7204        init_action_rules: &[usize],
7205        track_alt_numbers: bool,
7206    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7207        self.parse_atn_rule_with_runtime_options(
7208            atn,
7209            rule_index,
7210            ParserRuntimeOptions {
7211                init_action_rules,
7212                track_alt_numbers,
7213                ..ParserRuntimeOptions::default()
7214            },
7215        )
7216    }
7217
7218    /// Parses a generated rule with action replay and parser predicate support.
7219    ///
7220    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7221    /// target-template predicate semantics emitted by the generator. Missing
7222    /// entries are treated as true so unsupported predicate-free grammars keep
7223    /// the previous unconditional transition behavior.
7224    pub fn parse_atn_rule_with_runtime_options(
7225        &mut self,
7226        atn: &Atn,
7227        rule_index: usize,
7228        options: ParserRuntimeOptions<'_>,
7229    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7230        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7231    }
7232
7233    /// Parses a generated rule with action replay, parser predicate support,
7234    /// and an initial left-recursive precedence threshold.
7235    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7236        &mut self,
7237        atn: &Atn,
7238        rule_index: usize,
7239        precedence: i32,
7240        options: ParserRuntimeOptions<'_>,
7241    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7242        let ParserRuntimeOptions {
7243            init_action_rules,
7244            track_alt_numbers,
7245            track_context_alt_numbers,
7246            predicates,
7247            semantics,
7248            rule_args,
7249            member_actions,
7250            return_actions,
7251            unknown_predicate_policy,
7252        } = options;
7253        let capture_alt_numbers = track_alt_numbers || track_context_alt_numbers;
7254        if init_action_rules.is_empty()
7255            && !capture_alt_numbers
7256            && predicates.is_empty()
7257            && semantics.is_none()
7258            && rule_args.is_empty()
7259            && member_actions.is_empty()
7260            && return_actions.is_empty()
7261            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7262            && !atn_has_observable_action_transitions(atn)
7263            && !self.semantic_hooks.observes_parser_decisions()
7264            && (!self.semantic_hooks.observes_parser_predicates()
7265                || !atn_has_predicate_transitions(atn))
7266        {
7267            return self
7268                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7269                .map(|tree| (tree, Vec::new()));
7270        }
7271        if !self.semantic_hooks.observes_parser_decisions()
7272            && can_use_fast_predicate_recognizer(atn, &options)
7273        {
7274            self.unknown_predicate_policy = unknown_predicate_policy;
7275            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7276            let member_values = self.int_members.clone();
7277            let result = self
7278                .parse_atn_rule_with_precedence_inner(
7279                    atn,
7280                    rule_index,
7281                    precedence,
7282                    Some(FastPredicateContext {
7283                        predicates,
7284                        semantics,
7285                        member_values: &member_values,
7286                    }),
7287                    AltNumberTracking {
7288                        public: track_alt_numbers,
7289                        context: track_context_alt_numbers,
7290                    },
7291                )
7292                .map(|tree| (tree, Vec::new()));
7293            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7294                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7295            }
7296            return result;
7297        }
7298        self.unknown_predicate_policy = unknown_predicate_policy;
7299        // A generated parent may have already recorded unknown-predicate
7300        // coordinates before descending into this (interpreted) child. Clearing
7301        // unconditionally would drop them before the parent's public entry
7302        // surfaces them, so stash and restore around this call: recognition sees
7303        // only the hits it records itself (so the fail-loud check below reflects
7304        // this rule), and the parent's prior hits are merged back afterward.
7305        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7306        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7307            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7308        })?;
7309        let stop_state = atn
7310            .rule_to_stop_state()
7311            .get(rule_index)
7312            .filter(|state| *state != usize::MAX)
7313            .ok_or_else(|| {
7314                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7315            })?;
7316
7317        let start_index = self.current_visible_index();
7318        self.clear_prediction_diagnostics();
7319        self.reset_per_parse_caches();
7320        self.reset_recognition_arena();
7321        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7322        let invoking_state = self.pending_invoking_states.pop();
7323        let local_int_arg = invoking_state
7324            .and_then(|state| usize::try_from(state).ok())
7325            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7326        let mut visiting = BTreeSet::new();
7327        let mut memo = BTreeMap::new();
7328        let mut expected = ExpectedTokens::default();
7329        let member_values = self.int_members.clone();
7330        let return_values = BTreeMap::new();
7331        let outcomes = self.recognize_state(
7332            atn,
7333            RecognizeRequest {
7334                state_number: start_state,
7335                stop_state,
7336                index: start_index,
7337                rule_start_index: start_index,
7338                decision_start_index: None,
7339                init_action_rules: &init_action_rules,
7340                predicates,
7341                semantics,
7342                rule_args,
7343                member_actions,
7344                return_actions,
7345                local_int_arg,
7346                member_values,
7347                return_values,
7348                rule_alt_number: 0,
7349                track_alt_numbers: capture_alt_numbers,
7350                consumed_eof: false,
7351                committed_decision: false,
7352                precedence,
7353                depth: 0,
7354                recovery_symbols: BTreeSet::new(),
7355                recovery_state: None,
7356            },
7357            &mut visiting,
7358            &mut memo,
7359            &mut expected,
7360        );
7361        if let Some(error) = self.unknown_semantic_error() {
7362            self.report_token_source_errors();
7363            // Keep the recorded coordinates: when this interpreted rule is a
7364            // child of a generated parent, the parent's catch block recovers an
7365            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7366            // coordinate must survive on the parser for the top-level entry's
7367            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7368            // is handled by clearing at the top-level generated entry instead.
7369            return Err(error);
7370        }
7371        // Recognition recorded no unresolved coordinate of its own; merge the
7372        // parent's prior hits back so its public entry can still surface them.
7373        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7374        let Some(outcome) = select_best_outcome(
7375            outcomes.into_iter(),
7376            self.prediction_mode,
7377            &self.recognition_arena,
7378        ) else {
7379            let error = self.recognition_error(rule_index, start_index, &expected);
7380            self.record_syntax_errors(1);
7381            self.report_token_source_errors();
7382            return Err(error);
7383        };
7384
7385        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7386        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7387        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7388        self.report_token_source_errors();
7389        let mut actions = outcome.actions;
7390        if init_action_rules.contains(&rule_index) {
7391            actions.insert(
7392                0,
7393                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7394            );
7395        }
7396        let mut context =
7397            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7398        if track_alt_numbers {
7399            context.set_alt_number(outcome.alt_number.max(1));
7400        }
7401        if track_context_alt_numbers {
7402            context.set_context_alt_number(outcome.alt_number);
7403        }
7404        for (name, value) in outcome.return_values {
7405            context.set_int_return(name, value);
7406        }
7407        if let Some(token) = self.token_id_at(start_index) {
7408            self.set_context_start(&mut context, token);
7409        }
7410        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7411            self.set_context_stop(&mut context, token);
7412        }
7413        let live_root = if self.build_parse_trees {
7414            self.recognition_arena
7415                .fold_left_recursive_boundaries(outcome.nodes)
7416        } else {
7417            outcome.nodes
7418        };
7419        if self.build_parse_trees {
7420            let mut nodes = live_root;
7421            while let Some(link) = self.recognition_arena.link(nodes) {
7422                let child = self.arena_recognized_node_tree(
7423                    link.head,
7424                    track_alt_numbers,
7425                    track_context_alt_numbers,
7426                )?;
7427                self.tree.add_child(&mut context, child);
7428                nodes = link.tail;
7429            }
7430        }
7431        self.finish_recognition_arena(live_root, outcome.diagnostics);
7432        self.input.seek(outcome.index);
7433
7434        let tree = self.rule_node(context);
7435        self.release_tree_scratch_if_idle();
7436        Ok((tree, actions))
7437    }
7438
7439    /// Temporary parser entry used by generated parser methods while the parser
7440    /// ATN simulator is being implemented.
7441    ///
7442    /// This keeps generated parser crates buildable and gives us a stable method
7443    /// surface for every grammar rule. It intentionally accepts all remaining
7444    /// tokens into one rule context; it is not the final parser semantics.
7445    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
7446        let mut context = ParserRuleContext::new(rule_index, self.state());
7447        while self.la(1) != TOKEN_EOF {
7448            let token_type = self.la(1);
7449            let child = self.match_token(token_type)?;
7450            if self.build_parse_trees {
7451                self.tree.add_child(&mut context, child);
7452            }
7453        }
7454        if self.build_parse_trees {
7455            let child = self.match_eof()?;
7456            self.tree.add_child(&mut context, child);
7457        }
7458        let tree = self.rule_node(context);
7459        self.release_tree_scratch_if_idle();
7460        Ok(tree)
7461    }
7462
7463    /// Builds the parser error reported when no ATN path can reach the active
7464    /// rule stop state.
7465    fn recognition_error(
7466        &mut self,
7467        rule_index: usize,
7468        start_index: usize,
7469        expected: &ExpectedTokens,
7470    ) -> AntlrError {
7471        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
7472        self.input.seek(index);
7473        let current = self.input.lt(1);
7474        let line = current.as_ref().map(Token::line).unwrap_or_default();
7475        let column = current.as_ref().map(Token::column).unwrap_or_default();
7476        AntlrError::ParserError {
7477            line,
7478            column,
7479            message,
7480        }
7481    }
7482
7483    /// Builds the token index and ANTLR-compatible message for a failed rule.
7484    fn expected_error_message(
7485        &mut self,
7486        rule_index: usize,
7487        start_index: usize,
7488        expected: &ExpectedTokens,
7489    ) -> (usize, String) {
7490        let index = expected
7491            .index
7492            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
7493            .unwrap_or_else(|| self.input.index());
7494        self.input.seek(index);
7495        let current = self.input.lt(1);
7496        let message = if expected
7497            .no_viable
7498            .as_ref()
7499            .is_some_and(|no_viable| no_viable.error_index == index)
7500        {
7501            let start = expected
7502                .no_viable
7503                .as_ref()
7504                .map_or(start_index, |no_viable| no_viable.start_index);
7505            let text = display_input_text(&self.input.text(start, index));
7506            format!("no viable alternative at input '{text}'")
7507        } else if expected.symbols.is_empty() {
7508            if expected.index.is_some() {
7509                let found = current
7510                    .as_ref()
7511                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7512                if current
7513                    .as_ref()
7514                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7515                {
7516                    format!(
7517                        "missing {} at {found}",
7518                        self.expected_symbols_display(&expected.symbols)
7519                    )
7520                } else {
7521                    format!("mismatched input {found}")
7522                }
7523            } else {
7524                format!("no viable alternative while parsing rule {rule_index}")
7525            }
7526        } else {
7527            format!(
7528                "mismatched input {} expecting {}",
7529                current
7530                    .as_ref()
7531                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7532                self.expected_symbols_display(&expected.symbols)
7533            )
7534        };
7535        (index, message)
7536    }
7537
7538    /// Converts a failed child rule into a recovered outcome so the parent can
7539    /// continue after reporting the child diagnostic.
7540    fn child_rule_failure_recovery(
7541        &mut self,
7542        rule_index: usize,
7543        start_index: usize,
7544        sync_symbols: &BTreeSet<i32>,
7545        member_values: BTreeMap<usize, i64>,
7546        expected: &ExpectedTokens,
7547    ) -> Option<RecognizeOutcome> {
7548        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7549        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7550        let mut next_index = error_index;
7551        loop {
7552            let symbol = self.token_type_at(next_index);
7553            if sync_symbols.contains(&symbol) {
7554                if next_index == error_index {
7555                    return None;
7556                }
7557                break;
7558            }
7559            if symbol == TOKEN_EOF {
7560                break;
7561            }
7562            let after = self.consume_index(next_index, symbol);
7563            if after == next_index {
7564                break;
7565            }
7566            next_index = after;
7567        }
7568        let mut nodes = NodeSeqId::EMPTY;
7569        let error = self.arena_token_node(error_index, true);
7570        self.arena_prepend(&mut nodes, error);
7571        let diagnostics = self
7572            .recognition_arena
7573            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7574        Some(RecognizeOutcome {
7575            index: next_index,
7576            consumed_eof: false,
7577            alt_number: 0,
7578            member_values,
7579            return_values: BTreeMap::new(),
7580            diagnostics,
7581            decisions: Vec::new(),
7582            actions: Vec::new(),
7583            nodes,
7584        })
7585    }
7586
7587    /// Adapts the optional recovery result to the normal outcome list used by
7588    /// rule-call transitions.
7589    fn child_rule_failure_recovery_outcomes(
7590        &mut self,
7591        request: ChildRuleFailureRecovery<'_>,
7592    ) -> Vec<RecognizeOutcome> {
7593        let sync_symbols =
7594            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
7595        self.child_rule_failure_recovery(
7596            request.rule_index,
7597            request.start_index,
7598            &sync_symbols,
7599            request.member_values,
7600            request.expected,
7601        )
7602        .into_iter()
7603        .collect()
7604    }
7605
7606    /// Formats expected token types using ANTLR's single-token or set syntax.
7607    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
7608        expected_symbols_display(symbols, self.vocabulary())
7609    }
7610
7611    /// Returns the single-token deletion repair if the token after `index`
7612    /// satisfies the failed consuming transition.
7613    fn single_token_deletion(
7614        &mut self,
7615        transition: ParserTransition<'_>,
7616        index: usize,
7617        max_token_type: i32,
7618        expected_symbols: &BTreeSet<i32>,
7619    ) -> Option<(ParserDiagnostic, usize, i32)> {
7620        let current_symbol = self.token_type_at(index);
7621        if current_symbol == TOKEN_EOF {
7622            return None;
7623        }
7624        let next_index = self.consume_index(index, current_symbol);
7625        if next_index == index {
7626            return None;
7627        }
7628        let next_symbol = self.token_type_at(next_index);
7629        if !transition.matches(next_symbol, 1, max_token_type) {
7630            return None;
7631        }
7632        let transition_expected = transition_expected_symbols(transition, max_token_type);
7633        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7634            &transition_expected
7635        } else {
7636            expected_symbols
7637        });
7638        let current = self.token_at(index);
7639        let message = format!(
7640            "extraneous input {} expecting {expected_display}",
7641            current
7642                .as_ref()
7643                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7644        );
7645        Some((
7646            diagnostic_for_token(current, message),
7647            next_index,
7648            next_symbol,
7649        ))
7650    }
7651
7652    /// Returns the repair used when deleting the current token lets a recovery
7653    /// state continue with the following token.
7654    fn current_token_deletion(
7655        &mut self,
7656        index: usize,
7657        expected_symbols: &BTreeSet<i32>,
7658    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
7659        if expected_symbols.is_empty() {
7660            return None;
7661        }
7662        let current_symbol = self.token_type_at(index);
7663        if current_symbol == TOKEN_EOF {
7664            return None;
7665        }
7666        let current = self.token_at(index);
7667        let message = format!(
7668            "extraneous input {} expecting {}",
7669            current
7670                .as_ref()
7671                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7672            self.expected_symbols_display(expected_symbols)
7673        );
7674        let diagnostic = diagnostic_for_token(current, message);
7675        let mut skipped = Vec::new();
7676        let mut cursor = index;
7677        loop {
7678            let symbol = self.token_type_at(cursor);
7679            if symbol == TOKEN_EOF {
7680                return None;
7681            }
7682            skipped.push(cursor);
7683            let next_index = self.consume_index(cursor, symbol);
7684            if next_index == cursor {
7685                return None;
7686            }
7687            let next_symbol = self.token_type_at(next_index);
7688            if expected_symbols.contains(&next_symbol) {
7689                return Some((diagnostic, next_index, skipped));
7690            }
7691            cursor = next_index;
7692        }
7693    }
7694
7695    /// Returns the single-token insertion repair for a failed consuming
7696    /// transition. The caller validates the repair by continuing from the
7697    /// transition target at the same input index.
7698    fn single_token_insertion(
7699        &mut self,
7700        transition: ParserTransition<'_>,
7701        index: usize,
7702        max_token_type: i32,
7703        expected_symbols: &BTreeSet<i32>,
7704        follow_symbols: &BTreeSet<i32>,
7705    ) -> Option<(ParserDiagnostic, i32, String)> {
7706        let current_symbol = self.token_type_at(index);
7707        if !follow_symbols.contains(&current_symbol) {
7708            return None;
7709        }
7710        let transition_expected = transition_expected_symbols(transition, max_token_type);
7711        let token_type = transition_expected.iter().next().copied()?;
7712        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7713            &transition_expected
7714        } else {
7715            expected_symbols
7716        });
7717        let mut token_symbols = BTreeSet::new();
7718        token_symbols.insert(token_type);
7719        let missing_token_display = self.expected_symbols_display(&token_symbols);
7720        let current = self.token_at(index);
7721        let message = format!(
7722            "missing {expected_display} at {}",
7723            current
7724                .as_ref()
7725                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7726        );
7727        let text = format!("<missing {missing_token_display}>");
7728        Some((
7729            diagnostic_for_token(current.as_ref(), message),
7730            token_type,
7731            text,
7732        ))
7733    }
7734
7735    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
7736    /// skip the unexpected current token when the following token satisfies the
7737    /// transition that failed.
7738    fn fast_single_token_deletion_recovery(
7739        &mut self,
7740        recovery: FastRecoveryRequest<'_, '_>,
7741        predicate_context: Option<FastPredicateContext<'_>>,
7742    ) -> Vec<FastRecognizeOutcome> {
7743        let FastRecoveryRequest {
7744            atn,
7745            transition,
7746            expected_symbols,
7747            target,
7748            request,
7749            visiting,
7750            memo,
7751            expected,
7752        } = recovery;
7753        let FastRecognizeRequest {
7754            stop_state,
7755            index,
7756            rule_start_index,
7757            decision_start_index,
7758            precedence,
7759            depth,
7760            ..
7761        } = request;
7762        let Some((diagnostic, next_index, next_symbol)) =
7763            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7764        else {
7765            return Vec::new();
7766        };
7767        let after_next = self.consume_index(next_index, next_symbol);
7768        let empty_recovery = self.empty_recovery_symbols();
7769        self.recognize_state_fast(
7770            atn,
7771            FastRecognizeRequest {
7772                state_number: target,
7773                stop_state,
7774                index: after_next,
7775                rule_start_index,
7776                decision_start_index,
7777                precedence,
7778                depth: depth + 1,
7779                recovery_symbols: empty_recovery,
7780                recovery_state: None,
7781            },
7782            FastRecognizeScratch {
7783                predicate_context,
7784                visiting,
7785                memo,
7786                expected,
7787                native_depth: 0,
7788            },
7789        )
7790        .into_iter()
7791        .map(|mut outcome| {
7792            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7793            outcome.diagnostics = self
7794                .recognition_arena
7795                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7796            if self.fast_token_nodes_enabled {
7797                let token = self.arena_token_node(next_index, false);
7798                self.defer_fast_outcome_node(&mut outcome, token);
7799                let error = self.arena_token_node(index, true);
7800                self.defer_fast_outcome_node(&mut outcome, error);
7801            }
7802            outcome
7803        })
7804        .collect()
7805    }
7806
7807    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
7808    /// pretend the expected transition token was present and continue without
7809    /// consuming the current token.
7810    fn fast_single_token_insertion_recovery(
7811        &mut self,
7812        recovery: FastRecoveryRequest<'_, '_>,
7813        predicate_context: Option<FastPredicateContext<'_>>,
7814    ) -> Vec<FastRecognizeOutcome> {
7815        let FastRecoveryRequest {
7816            atn,
7817            transition,
7818            expected_symbols,
7819            target,
7820            request,
7821            visiting,
7822            memo,
7823            expected,
7824        } = recovery;
7825        let FastRecognizeRequest {
7826            stop_state,
7827            index,
7828            rule_start_index,
7829            decision_start_index,
7830            precedence,
7831            depth,
7832            ..
7833        } = request;
7834        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
7835        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7836            transition,
7837            index,
7838            atn.max_token_type(),
7839            &expected_symbols,
7840            &follow_symbols,
7841        ) else {
7842            return Vec::new();
7843        };
7844        let empty_recovery = self.empty_recovery_symbols();
7845        self.recognize_state_fast(
7846            atn,
7847            FastRecognizeRequest {
7848                state_number: target,
7849                stop_state,
7850                index,
7851                rule_start_index,
7852                decision_start_index,
7853                precedence,
7854                depth: depth + 1,
7855                recovery_symbols: empty_recovery,
7856                recovery_state: None,
7857            },
7858            FastRecognizeScratch {
7859                predicate_context,
7860                visiting,
7861                memo,
7862                expected,
7863                native_depth: 0,
7864            },
7865        )
7866        .into_iter()
7867        .map(|mut outcome| {
7868            outcome.diagnostics = self
7869                .recognition_arena
7870                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7871            let missing = self.arena_missing_token_node(token_type, index, text.clone());
7872            self.defer_fast_outcome_node(&mut outcome, missing);
7873            outcome
7874        })
7875        .collect()
7876    }
7877
7878    /// Retries the current fast-recognition state after deleting one
7879    /// unexpected token that precedes a valid loop or block continuation.
7880    fn fast_current_token_deletion_recovery(
7881        &mut self,
7882        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
7883        predicate_context: Option<FastPredicateContext<'_>>,
7884    ) -> Vec<FastRecognizeOutcome> {
7885        let FastCurrentTokenDeletionRequest {
7886            atn,
7887            expected_symbols,
7888            mut request,
7889            visiting,
7890            memo,
7891            expected,
7892        } = recovery;
7893        if request.index == request.rule_start_index {
7894            return Vec::new();
7895        }
7896        let Some((diagnostic, next_index, skipped)) =
7897            self.current_token_deletion(request.index, &expected_symbols)
7898        else {
7899            return Vec::new();
7900        };
7901        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7902        request.index = next_index;
7903        request.depth += 1;
7904        request.recovery_state = None;
7905        self.recognize_state_fast(
7906            atn,
7907            request,
7908            FastRecognizeScratch {
7909                predicate_context,
7910                visiting,
7911                memo,
7912                expected,
7913                native_depth: 0,
7914            },
7915        )
7916        .into_iter()
7917        .map(|mut outcome| {
7918            outcome.diagnostics = self
7919                .recognition_arena
7920                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7921            for index in skipped.iter().rev() {
7922                let error = self.arena_token_node(*index, true);
7923                self.defer_fast_outcome_node(&mut outcome, error);
7924            }
7925            outcome
7926        })
7927        .collect()
7928    }
7929
7930    /// Converts a failed child rule into a recovered fast-recognizer outcome so
7931    /// the parent can keep its child rule context and continue at a sync token.
7932    fn fast_child_rule_failure_recovery(
7933        &mut self,
7934        rule_index: usize,
7935        start_index: usize,
7936        sync_symbols: &BTreeSet<i32>,
7937        expected: &ExpectedTokens,
7938    ) -> Option<FastRecognizeOutcome> {
7939        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7940        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7941        let mut next_index = error_index;
7942        loop {
7943            let symbol = self.token_type_at(next_index);
7944            if sync_symbols.contains(&symbol) {
7945                if next_index == error_index {
7946                    return None;
7947                }
7948                break;
7949            }
7950            if symbol == TOKEN_EOF {
7951                break;
7952            }
7953            let after = self.consume_index(next_index, symbol);
7954            if after == next_index {
7955                break;
7956            }
7957            next_index = after;
7958        }
7959        let diagnostics = self
7960            .recognition_arena
7961            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7962        let mut nodes = NodeSeqId::EMPTY;
7963        if self.fast_token_nodes_enabled {
7964            let error = self.arena_token_node(error_index, true);
7965            self.arena_prepend(&mut nodes, error);
7966        }
7967        Some(FastRecognizeOutcome {
7968            index: next_index,
7969            consumed_eof: false,
7970            diagnostics,
7971            deferred_nodes: FastDeferredNodeId::EMPTY,
7972            nodes,
7973        })
7974    }
7975
7976    /// Adapts the optional child-rule recovery result to the fast-recognizer
7977    /// outcome list used by rule-call transitions.
7978    fn fast_child_rule_failure_recovery_outcomes(
7979        &mut self,
7980        request: FastChildRuleFailureRecoveryRequest<'_>,
7981    ) -> Vec<FastRecognizeOutcome> {
7982        let FastChildRuleFailureRecoveryRequest {
7983            atn,
7984            rule_index,
7985            start_index,
7986            follow_state,
7987            stop_state,
7988            expected,
7989        } = request;
7990        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
7991        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
7992            .into_iter()
7993            .collect()
7994    }
7995
7996    fn defer_fast_outcome_node(
7997        &mut self,
7998        outcome: &mut FastRecognizeOutcome,
7999        node: RecognizedNodeId,
8000    ) {
8001        if outcome.deferred_nodes.is_empty() {
8002            self.arena_prepend(&mut outcome.nodes, node);
8003            return;
8004        }
8005        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
8006        let fragment = self.recognition_arena.deferred_fragment(fragment);
8007        outcome.deferred_nodes = self
8008            .recognition_arena
8009            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
8010    }
8011
8012    fn defer_fast_outcome_alternative(
8013        &mut self,
8014        outcome: &mut FastRecognizeOutcome,
8015        alt_number: usize,
8016    ) {
8017        let alternative = self.recognition_arena.deferred_alternative(alt_number);
8018        outcome.deferred_nodes = self
8019            .recognition_arena
8020            .concat_deferred_nodes(alternative, outcome.deferred_nodes);
8021    }
8022
8023    fn defer_fast_outcome_boundary(
8024        &mut self,
8025        outcome: &mut FastRecognizeOutcome,
8026        rule_index: usize,
8027    ) {
8028        let boundary = self
8029            .recognition_arena
8030            .deferred_left_recursive_boundary(rule_index);
8031        outcome.deferred_nodes = self
8032            .recognition_arena
8033            .concat_deferred_nodes(boundary, outcome.deferred_nodes);
8034    }
8035
8036    fn materialize_fast_deferred_nodes(
8037        &mut self,
8038        root: FastDeferredNodeId,
8039        initial_suffix: NodeSeqId,
8040    ) -> (NodeSeqId, usize) {
8041        if root.is_empty() {
8042            return (initial_suffix, 0);
8043        }
8044
8045        enum Frame {
8046            Visit(FastDeferredNodeId),
8047            ContinuePrefix(FastDeferredNodeId),
8048            FinishRule {
8049                rule: FastDeferredRule,
8050                parent_suffix: NodeSeqId,
8051                parent_alt_number: u32,
8052                parent_pending_boundary: Option<RecognizedNodeId>,
8053            },
8054        }
8055
8056        let mut result = initial_suffix;
8057        // The rope is visited suffix-first while nodes are prepended. Later
8058        // alternatives arrive first, so earlier markers overwrite them; a
8059        // boundary redirects those earlier markers to the wrapped context.
8060        let mut alt_number = 0;
8061        let mut pending_boundary = None;
8062        let mut pending = Vec::with_capacity(16);
8063        pending.push(Frame::Visit(root));
8064        let mut fragment_nodes = Vec::new();
8065        while let Some(frame) = pending.pop() {
8066            match frame {
8067                Frame::Visit(deferred) => {
8068                    if deferred.is_empty() {
8069                        continue;
8070                    }
8071
8072                    match self.recognition_arena.deferred_node(deferred) {
8073                        FastDeferredNode::Fragment(sequence) => {
8074                            fragment_nodes.clear();
8075                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
8076                            while let Some(node) = fragment_nodes.pop() {
8077                                self.arena_prepend(&mut result, node);
8078                            }
8079                        }
8080                        FastDeferredNode::Rule(rule) => {
8081                            let rule = self.recognition_arena.deferred_rule(rule);
8082                            let parent_suffix = result;
8083                            let parent_alt_number = alt_number;
8084                            let parent_pending_boundary = pending_boundary;
8085                            result = rule.children;
8086                            alt_number = 0;
8087                            pending_boundary = None;
8088                            pending.push(Frame::FinishRule {
8089                                rule,
8090                                parent_suffix,
8091                                parent_alt_number,
8092                                parent_pending_boundary,
8093                            });
8094                            pending.push(Frame::Visit(rule.deferred_children));
8095                        }
8096                        FastDeferredNode::Alternative(selected) => {
8097                            if let Some(boundary) = pending_boundary {
8098                                self.recognition_arena
8099                                    .set_boundary_alt_number(boundary, selected);
8100                            } else {
8101                                alt_number = selected;
8102                            }
8103                        }
8104                        FastDeferredNode::LeftRecursiveBoundary { rule_index } => {
8105                            let boundary = self.arena_boundary_node(rule_index as usize, 0);
8106                            self.arena_prepend(&mut result, boundary);
8107                            pending_boundary = Some(boundary);
8108                        }
8109                        FastDeferredNode::Concat {
8110                            prefix,
8111                            suffix: deferred_suffix,
8112                        } => {
8113                            pending.push(Frame::ContinuePrefix(prefix));
8114                            pending.push(Frame::Visit(deferred_suffix));
8115                        }
8116                    }
8117                }
8118                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
8119                Frame::FinishRule {
8120                    rule,
8121                    parent_suffix,
8122                    parent_alt_number,
8123                    parent_pending_boundary,
8124                } => {
8125                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
8126                        rule_index: rule.rule_index,
8127                        invoking_state: rule.invoking_state,
8128                        alt_number,
8129                        start_index: rule.start_index,
8130                        stop_index: rule.stop_index,
8131                        return_values: None,
8132                        children: result,
8133                    });
8134                    result = parent_suffix;
8135                    self.arena_prepend(&mut result, node);
8136                    alt_number = parent_alt_number;
8137                    pending_boundary = parent_pending_boundary;
8138                }
8139            }
8140        }
8141        (result, alt_number as usize)
8142    }
8143
8144    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) -> usize {
8145        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
8146        let (nodes, alt_number) =
8147            self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
8148        outcome.nodes = nodes;
8149        alt_number
8150    }
8151
8152    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
8153    /// heap work instead of the native call stack.
8154    fn recognize_repetition_fast(
8155        &mut self,
8156        atn: &Atn,
8157        request: &FastRecognizeRequest,
8158        shape: FastRepetitionShape,
8159        scratch: FastRecognizeScratch<'_, '_>,
8160    ) -> Vec<FastRecognizeOutcome> {
8161        let FastRecognizeScratch {
8162            predicate_context,
8163            visiting,
8164            memo,
8165            expected,
8166            native_depth,
8167        } = scratch;
8168        let lookahead = if self.fast_first_set_prefilter {
8169            atn.state(request.state_number).and_then(|state| {
8170                state
8171                    .rule_index()
8172                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8173                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
8174            })
8175        } else {
8176            None
8177        };
8178        let (enter_alt_number, exit_alt_number) = if self.fast_track_alt_numbers {
8179            let state = atn
8180                .state(request.state_number)
8181                .expect("repetition request state must exist");
8182            (
8183                next_alt_number(state, 2, shape.enter_transition_index, 0, true),
8184                next_alt_number(state, 2, shape.exit_transition_index, 0, true),
8185            )
8186        } else {
8187            (0, 0)
8188        };
8189        let mut work = Vec::with_capacity(2);
8190        push_fast_repetition_work(
8191            &mut work,
8192            shape,
8193            FastRepetitionPath {
8194                index: request.index,
8195                deferred_nodes: FastDeferredNodeId::EMPTY,
8196                diagnostics: DiagnosticSeqId::EMPTY,
8197                consumed_eof: false,
8198            },
8199            lookahead.as_deref(),
8200            self.token_type_at(request.index),
8201        );
8202        let mut coordinates = FastRepetitionCoordinates::new(request.index);
8203        let mut outcomes = Vec::new();
8204        while let Some(item) = work.pop() {
8205            match item {
8206                FastRepetitionWork::Enter(path) => {
8207                    if !coordinates.insert_entered(path) {
8208                        continue;
8209                    }
8210                    let path_nodes = if enter_alt_number == 0 {
8211                        path.deferred_nodes
8212                    } else {
8213                        let alternative = self
8214                            .recognition_arena
8215                            .deferred_alternative(enter_alt_number);
8216                        self.recognition_arena
8217                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8218                    };
8219                    let body_outcomes = self.recognize_state_fast(
8220                        atn,
8221                        FastRecognizeRequest {
8222                            state_number: shape.enter_target,
8223                            stop_state: shape.body_stop_state,
8224                            index: path.index,
8225                            rule_start_index: request.rule_start_index,
8226                            decision_start_index: request.decision_start_index,
8227                            precedence: request.precedence,
8228                            depth: request.depth.saturating_add(1),
8229                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8230                            recovery_state: request.recovery_state,
8231                        },
8232                        FastRecognizeScratch {
8233                            predicate_context,
8234                            visiting: &mut *visiting,
8235                            memo: &mut *memo,
8236                            expected: &mut *expected,
8237                            native_depth: native_depth + 1,
8238                        },
8239                    );
8240                    for body in body_outcomes.into_iter().rev() {
8241                        // ANTLR rejects nullable repetition bodies. Keep the
8242                        // interpreter bounded for malformed or recovered ATNs
8243                        // by mirroring the existing same-coordinate cycle cut.
8244                        if body.index <= path.index {
8245                            continue;
8246                        }
8247                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
8248                        let body_nodes = self
8249                            .recognition_arena
8250                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
8251                        let deferred_nodes = self
8252                            .recognition_arena
8253                            .concat_deferred_nodes(path_nodes, body_nodes);
8254                        let next_path = FastRepetitionPath {
8255                            index: body.index,
8256                            deferred_nodes,
8257                            diagnostics: self
8258                                .recognition_arena
8259                                .concat_diagnostics(path.diagnostics, body.diagnostics),
8260                            consumed_eof: path.consumed_eof || body.consumed_eof,
8261                        };
8262                        let symbol = self.token_type_at(next_path.index);
8263                        push_fast_repetition_work(
8264                            &mut work,
8265                            shape,
8266                            next_path,
8267                            lookahead.as_deref(),
8268                            symbol,
8269                        );
8270                    }
8271                }
8272                FastRepetitionWork::Exit(path) => {
8273                    if !coordinates.insert_exited(path) {
8274                        continue;
8275                    }
8276                    let path_nodes = if exit_alt_number == 0 {
8277                        path.deferred_nodes
8278                    } else {
8279                        let alternative =
8280                            self.recognition_arena.deferred_alternative(exit_alt_number);
8281                        self.recognition_arena
8282                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8283                    };
8284                    let suffixes = self.recognize_state_fast(
8285                        atn,
8286                        FastRecognizeRequest {
8287                            state_number: shape.exit_target,
8288                            stop_state: request.stop_state,
8289                            index: path.index,
8290                            rule_start_index: request.rule_start_index,
8291                            decision_start_index: request.decision_start_index,
8292                            precedence: request.precedence,
8293                            depth: request.depth.saturating_add(1),
8294                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8295                            recovery_state: request.recovery_state,
8296                        },
8297                        FastRecognizeScratch {
8298                            predicate_context,
8299                            visiting: &mut *visiting,
8300                            memo: &mut *memo,
8301                            expected: &mut *expected,
8302                            native_depth: native_depth + 1,
8303                        },
8304                    );
8305                    for mut outcome in suffixes {
8306                        outcome.deferred_nodes = self
8307                            .recognition_arena
8308                            .concat_deferred_nodes(path_nodes, outcome.deferred_nodes);
8309                        outcome.diagnostics = self
8310                            .recognition_arena
8311                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8312                        outcome.consumed_eof |= path.consumed_eof;
8313                        outcomes.push(outcome);
8314                    }
8315                }
8316            }
8317        }
8318        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8319        outcomes
8320    }
8321
8322    /// Attempts to reach `stop_state` from `state_number` without committing
8323    /// token consumption to the parser's public stream position.
8324    fn recognize_state_fast(
8325        &mut self,
8326        atn: &Atn,
8327        request: FastRecognizeRequest,
8328        scratch: FastRecognizeScratch<'_, '_>,
8329    ) -> Vec<FastRecognizeOutcome> {
8330        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
8331            return self.recognize_state_fast_inner(atn, request, scratch);
8332        }
8333        self.recognize_state_fast_checked(atn, request, scratch)
8334    }
8335
8336    #[inline(never)]
8337    fn recognize_state_fast_checked(
8338        &mut self,
8339        atn: &Atn,
8340        request: FastRecognizeRequest,
8341        mut scratch: FastRecognizeScratch<'_, '_>,
8342    ) -> Vec<FastRecognizeOutcome> {
8343        scratch.native_depth = 1;
8344        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
8345            self.recognize_state_fast_inner(atn, request, scratch)
8346        })
8347    }
8348
8349    #[allow(clippy::too_many_lines)]
8350    fn recognize_state_fast_inner(
8351        &mut self,
8352        atn: &Atn,
8353        request: FastRecognizeRequest,
8354        scratch: FastRecognizeScratch<'_, '_>,
8355    ) -> Vec<FastRecognizeOutcome> {
8356        #[cfg(feature = "perf-counters")]
8357        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8358        let FastRecognizeScratch {
8359            predicate_context,
8360            visiting,
8361            memo,
8362            expected,
8363            native_depth,
8364        } = scratch;
8365        let FastRecognizeRequest {
8366            mut state_number,
8367            stop_state,
8368            mut index,
8369            rule_start_index,
8370            decision_start_index,
8371            precedence,
8372            mut depth,
8373            recovery_symbols,
8374            recovery_state,
8375        } = request;
8376        let max_token_type = atn.max_token_type();
8377        // Walk straight-line epsilon chains in a loop instead of recursing
8378        // into `recognize_state_fast` for each intermediate state. ATN
8379        // serialization places long sequences of `BasicBlock` epsilon
8380        // transitions between decisions: turning that chain into a loop
8381        // collapses many recursive calls (and their memo lookups, vec
8382        // allocations, and visit-set churn) into a single function frame.
8383        // The loop exits as soon as we hit the original state's logic
8384        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8385        // precedence) so existing fanout, recovery, and memoization still
8386        // apply unchanged.
8387        //
8388        // The inline case also handles single-atom-match states on the
8389        // happy-pass path: when the lone consuming transition matches the
8390        // current lookahead, advance the index and continue without paying
8391        // for a full `recognize_state_fast` recursion. We track tokens we
8392        // consumed inline in `inline_consumed_tokens` so they can be
8393        // prepended onto the eventual outcome list once we hit a state
8394        // whose handling falls outside this fast loop.
8395        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8396        let mut inline_consumed_eof = false;
8397        loop {
8398            if depth > RECOGNITION_DEPTH_LIMIT {
8399                return Vec::new();
8400            }
8401            if state_number == stop_state {
8402                let mut nodes = NodeSeqId::EMPTY;
8403                if self.fast_token_nodes_enabled {
8404                    for token_index in inline_consumed_tokens.iter().rev() {
8405                        let token = self.arena_token_node(*token_index, false);
8406                        self.arena_prepend(&mut nodes, token);
8407                    }
8408                }
8409                return vec![FastRecognizeOutcome {
8410                    index,
8411                    consumed_eof: inline_consumed_eof,
8412                    diagnostics: DiagnosticSeqId::EMPTY,
8413                    deferred_nodes: FastDeferredNodeId::EMPTY,
8414                    nodes,
8415                }];
8416            }
8417            let Some(state) = atn.state(state_number) else {
8418                return Vec::new();
8419            };
8420            let transitions = state.transitions();
8421            if transitions.len() == 1 && !state.precedence_rule_decision() {
8422                let transition = transitions
8423                    .first()
8424                    .expect("single transition checked above");
8425                let transition_kind = transition.kind();
8426                let target = transition.target();
8427                match transition_kind {
8428                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
8429                        if left_recursive_boundary(atn, state, target).is_none() =>
8430                    {
8431                        #[cfg(feature = "perf-counters")]
8432                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8433                        state_number = target;
8434                        depth += 1;
8435                        continue;
8436                    }
8437                    ParserTransitionKind::Predicate
8438                        if left_recursive_boundary(atn, state, target).is_none() =>
8439                    {
8440                        #[cfg(feature = "perf-counters")]
8441                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8442                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
8443                        {
8444                            record_predicate_no_viable(expected, decision_start_index, index);
8445                            return Vec::new();
8446                        }
8447                        state_number = target;
8448                        depth += 1;
8449                        continue;
8450                    }
8451                    ParserTransitionKind::Precedence
8452                        if packed_i32(transition.arg0()) >= precedence
8453                            && left_recursive_boundary(atn, state, target).is_none() =>
8454                    {
8455                        #[cfg(feature = "perf-counters")]
8456                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8457                        state_number = target;
8458                        depth += 1;
8459                        continue;
8460                    }
8461                    // Single-atom / range / set / wildcard / not-set states
8462                    // are common (~17K of ~125K calls on C#) and almost
8463                    // always succeed in pass 1: no fanout, no recovery, no
8464                    // diagnostics. Inline the token match and continue
8465                    // walking instead of recursing — the recursive path
8466                    // would just allocate a Vec, build one outcome, prepend
8467                    // a Token node, and return. Skip pass 2 (recovery
8468                    // enabled): there the failure branch matters and the
8469                    // existing recursive code records expected symbols.
8470                    ParserTransitionKind::Atom
8471                    | ParserTransitionKind::Range
8472                    | ParserTransitionKind::Set
8473                    | ParserTransitionKind::NotSet
8474                    | ParserTransitionKind::Wildcard
8475                        if !self.fast_recovery_enabled =>
8476                    {
8477                        let symbol = self.token_type_at(index);
8478                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8479                            #[cfg(feature = "perf-counters")]
8480                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8481                            if self.fast_token_nodes_enabled {
8482                                inline_consumed_tokens.push(index);
8483                            }
8484                            inline_consumed_eof |= symbol == TOKEN_EOF;
8485                            index = self.consume_index(index, symbol);
8486                            state_number = target;
8487                            depth += 1;
8488                            continue;
8489                        }
8490                        // Fall through to break and let the regular
8491                        // body handle the no-match case (returns empty).
8492                    }
8493                    _ => {}
8494                }
8495            }
8496            break;
8497        }
8498        // If we collected token nodes inline but bail to the recursive
8499        // body (decision state, rule call, etc.), the outcomes returned
8500        // below will need those token nodes prepended.
8501        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
8502        let Some(state) = atn.state(state_number) else {
8503            return Vec::new();
8504        };
8505        let transitions = state.transitions();
8506        let transition_count = transitions.len();
8507        if !self.fast_recovery_enabled
8508            && let Some(shape) = fast_repetition_shape(atn, state)
8509        {
8510            let mut outcomes = self.recognize_repetition_fast(
8511                atn,
8512                &FastRecognizeRequest {
8513                    state_number,
8514                    stop_state,
8515                    index,
8516                    rule_start_index,
8517                    decision_start_index,
8518                    precedence,
8519                    depth,
8520                    recovery_symbols: Rc::clone(&recovery_symbols),
8521                    recovery_state,
8522                },
8523                shape,
8524                FastRecognizeScratch {
8525                    predicate_context,
8526                    visiting: &mut *visiting,
8527                    memo: &mut *memo,
8528                    expected: &mut *expected,
8529                    native_depth: native_depth + 1,
8530                },
8531            );
8532            if inline_pending {
8533                for outcome in &mut outcomes {
8534                    outcome.consumed_eof |= inline_consumed_eof;
8535                    if self.fast_token_nodes_enabled {
8536                        for token_index in inline_consumed_tokens.iter().rev() {
8537                            let token = self.arena_token_node(*token_index, false);
8538                            self.defer_fast_outcome_node(outcome, token);
8539                        }
8540                    }
8541                }
8542            }
8543            return outcomes;
8544        }
8545        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
8546        // fields and the rule/decision boundary indices are pure plumbing —
8547        // they only affect the recovery branch and the no-viable diagnostic
8548        // recording, neither of which fires when recovery is off. Zeroing
8549        // them in the memo key collapses calls that visit the same
8550        // `(state, index)` from different rule-call sites onto one cache
8551        // entry, which is the dominant cost on large grammars (e.g. C#) where
8552        // many rules eventually delegate into the same `expression` /
8553        // `primary_expression` / `type` branches.
8554        let key = if self.fast_recovery_enabled {
8555            FastRecognizeKey {
8556                state_number,
8557                stop_state,
8558                index,
8559                rule_start_index,
8560                decision_start_index,
8561                precedence,
8562                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
8563                recovery_state,
8564            }
8565        } else {
8566            FastRecognizeKey {
8567                state_number,
8568                stop_state,
8569                index,
8570                rule_start_index: 0,
8571                decision_start_index: None,
8572                precedence,
8573                recovery_symbols_id: 0,
8574                recovery_state: None,
8575            }
8576        };
8577        // Once the clean-pass probe has established that coordinates do not
8578        // repeat, stop paying for the full memo table. Recovery always keeps
8579        // memoization because cached failures carry diagnostics, while
8580        // repeat-heavy clean parses promote before reaching sparse mode.
8581        let memo_lookup_enabled = self.fast_recovery_enabled
8582            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
8583        if memo_lookup_enabled {
8584            if let Some(outcomes) = memo.get(&key) {
8585                #[cfg(feature = "perf-counters")]
8586                {
8587                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
8588                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
8589                }
8590                // Materialize a fresh `Vec` from the cached slice; the caller
8591                // mutates per-outcome state (eof flags, prepended nodes) so we
8592                // can't hand them the shared backing.
8593                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
8594                    let inline_eof = inline_consumed_eof;
8595                    let inline_tokens = &inline_consumed_tokens;
8596                    return outcomes
8597                        .iter()
8598                        .copied()
8599                        .map(|mut outcome| {
8600                            if inline_eof {
8601                                outcome.consumed_eof = true;
8602                            }
8603                            if self.fast_token_nodes_enabled {
8604                                for token_index in inline_tokens.iter().rev() {
8605                                    let token = self.arena_token_node(*token_index, false);
8606                                    self.defer_fast_outcome_node(&mut outcome, token);
8607                                }
8608                            }
8609                            outcome
8610                        })
8611                        .collect();
8612                }
8613                return outcomes.to_vec();
8614            }
8615            #[cfg(feature = "perf-counters")]
8616            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
8617        }
8618
8619        // Cycle detection: clean recognition keeps the narrow static cycle
8620        // guard used on hot paths. Recovery needs the broader epsilon-state
8621        // guard because an otherwise non-nullable loop body can recover as an
8622        // empty child at EOF and re-enter the loop at the same token.
8623        let needs_cycle_guard = if self.fast_recovery_enabled {
8624            transitions.iter().any(ParserTransition::is_epsilon)
8625        } else {
8626            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
8627        };
8628        #[cfg(feature = "perf-counters")]
8629        if needs_cycle_guard {
8630            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
8631        } else {
8632            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
8633            match state
8634                .transitions()
8635                .first()
8636                .expect("single-transition path requires one transition")
8637                .data()
8638            {
8639                Transition::Rule { .. } => {
8640                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
8641                }
8642                Transition::Atom { .. }
8643                | Transition::Range { .. }
8644                | Transition::Set { .. }
8645                | Transition::NotSet { .. }
8646                | Transition::Wildcard { .. } => {
8647                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
8648                }
8649                _ => {
8650                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
8651                }
8652            }
8653        }
8654        let has_inserted_cycle_guard = if needs_cycle_guard {
8655            if !visiting.insert(key.clone()) {
8656                #[cfg(feature = "perf-counters")]
8657                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
8658                return Vec::new();
8659            }
8660            true
8661        } else {
8662            false
8663        };
8664        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
8665            Some(index)
8666        } else {
8667            decision_start_index
8668        };
8669        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
8670            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
8671        } else {
8672            (Rc::clone(&recovery_symbols), recovery_state)
8673        };
8674
8675        // Lookahead-based pruning. At a multi-alternative state we cache the
8676        // look-1 set of every outgoing transition; on visit we keep only the
8677        // transitions whose look-1 can accept the current lookahead (or that
8678        // can be reached without consuming and so could legitimately match a
8679        // shorter input). This is the main speedup vs. blind speculative
8680        // recursion: it lets each visit fan out only to the alternatives that
8681        // could possibly contribute a clean parse, mirroring the SLL phase of
8682        // ANTLR's adaptive prediction.
8683        //
8684        // Pruning is skipped at:
8685        //   * rule-start states (a child rule call may need every internal
8686        //     transition to surface single-token recovery diagnostics that
8687        //     ANTLR's reference parser emits at the rule's first consuming
8688        //     transition; the FIRST-set retry path turns the prefilter off
8689        //     entirely so let's keep this lightweight too),
8690        //   * left-recursive precedence loops (the precedence transition's
8691        //     gating is dynamic),
8692        //   * states with too few alternatives to benefit.
8693        let lookahead_filter = if transition_count > 1
8694            && self.fast_first_set_prefilter
8695            && !state.precedence_rule_decision()
8696            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
8697        {
8698            state
8699                .rule_index()
8700                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8701                .map(|rule_stop| {
8702                    let symbol = self.token_type_at(index);
8703                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
8704                    (symbol, entry)
8705                })
8706        } else {
8707            None
8708        };
8709        // LL(1) fast path: when the FIRST sets for the decision are disjoint
8710        // and none is nullable, the lookahead deterministically selects one
8711        // alternative. The recursive recognizer can then commit to that single
8712        // alt without iterating every transition through `should_skip_via_lookahead`
8713        // — saving (transition_count - 1) filter probes per visit.
8714        //
8715        // Result is cached per `(state, lookahead_token)` on the parser
8716        // instance, so subsequent visits skip the FIRST-set scan entirely.
8717        let ll1_only_alt: Option<usize> = if transition_count > 1
8718            && let Some((symbol, entry)) = lookahead_filter.as_ref()
8719        {
8720            let key = (state.state_number(), *symbol);
8721            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
8722                cached
8723            } else {
8724                let result = ll1_unique_alt(entry, *symbol);
8725                self.ll1_decision_cache.insert(key, result);
8726                result
8727            }
8728        } else {
8729            None
8730        };
8731        let lookahead_filter = lookahead_filter.as_ref();
8732        // Pre-size only when we expect at least one outcome to land — most
8733        // single-transition fall-throughs (the loop above didn't catch
8734        // because they're atom/rule/predicate) push at most one entry, so
8735        // reserving one slot avoids a reallocation while keeping the
8736        // unused-slot waste at one element.
8737        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
8738        for (transition_index, transition) in transitions.iter().enumerate() {
8739            if let Some(alt) = ll1_only_alt {
8740                // LL(1) determinism: skip every alt except the chosen one.
8741                if alt != transition_index {
8742                    continue;
8743                }
8744            }
8745            let transition_kind = transition.kind();
8746            if ll1_only_alt.is_none()
8747                && should_skip_via_lookahead(
8748                    transition_kind,
8749                    transition_index,
8750                    lookahead_filter,
8751                    index,
8752                    self.fast_recovery_enabled,
8753                    expected,
8754                )
8755            {
8756                continue;
8757            }
8758            let target = transition.target();
8759            let outcomes_before_transition = outcomes.len();
8760            let left_recursive_boundary = match transition_kind {
8761                ParserTransitionKind::Epsilon
8762                | ParserTransitionKind::Action
8763                | ParserTransitionKind::Predicate
8764                | ParserTransitionKind::Precedence => left_recursive_boundary(atn, state, target),
8765                ParserTransitionKind::Atom
8766                | ParserTransitionKind::Range
8767                | ParserTransitionKind::Set
8768                | ParserTransitionKind::NotSet
8769                | ParserTransitionKind::Wildcard
8770                | ParserTransitionKind::Rule => None,
8771            };
8772            match transition_kind {
8773                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
8774                    #[cfg(feature = "perf-counters")]
8775                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8776                    outcomes.extend(self.recognize_state_fast(
8777                        atn,
8778                        FastRecognizeRequest {
8779                            state_number: target,
8780                            stop_state,
8781                            index,
8782                            rule_start_index,
8783                            decision_start_index: next_decision_start_index,
8784                            precedence,
8785                            depth: depth + 1,
8786                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8787                            recovery_state: epsilon_recovery_state,
8788                        },
8789                        FastRecognizeScratch {
8790                            predicate_context,
8791                            visiting,
8792                            memo,
8793                            expected,
8794                            native_depth: native_depth + 1,
8795                        },
8796                    ));
8797                }
8798                ParserTransitionKind::Predicate => {
8799                    #[cfg(feature = "perf-counters")]
8800                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8801                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
8802                        outcomes.extend(self.recognize_state_fast(
8803                            atn,
8804                            FastRecognizeRequest {
8805                                state_number: target,
8806                                stop_state,
8807                                index,
8808                                rule_start_index,
8809                                decision_start_index: next_decision_start_index,
8810                                precedence,
8811                                depth: depth + 1,
8812                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8813                                recovery_state: epsilon_recovery_state,
8814                            },
8815                            FastRecognizeScratch {
8816                                predicate_context,
8817                                visiting,
8818                                memo,
8819                                expected,
8820                                native_depth: native_depth + 1,
8821                            },
8822                        ));
8823                    } else {
8824                        record_predicate_no_viable(expected, next_decision_start_index, index);
8825                    }
8826                }
8827                ParserTransitionKind::Precedence => {
8828                    let transition_precedence = packed_i32(transition.arg0());
8829                    if transition_precedence >= precedence {
8830                        outcomes.extend(self.recognize_state_fast(
8831                            atn,
8832                            FastRecognizeRequest {
8833                                state_number: target,
8834                                stop_state,
8835                                index,
8836                                rule_start_index,
8837                                decision_start_index: next_decision_start_index,
8838                                precedence,
8839                                depth: depth + 1,
8840                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8841                                recovery_state: epsilon_recovery_state,
8842                            },
8843                            FastRecognizeScratch {
8844                                predicate_context,
8845                                visiting,
8846                                memo,
8847                                expected,
8848                                native_depth: native_depth + 1,
8849                            },
8850                        ));
8851                    }
8852                }
8853                ParserTransitionKind::Rule => {
8854                    let rule_index = transition.arg0() as usize;
8855                    let follow_state = transition.arg1() as usize;
8856                    let rule_precedence = packed_i32(transition.arg2());
8857                    #[cfg(feature = "perf-counters")]
8858                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
8859                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
8860                        continue;
8861                    };
8862                    // Lookahead-based pruning. The recognizer would otherwise
8863                    // explore every speculative rule call, producing exponential
8864                    // work on grammars with many epsilon-reachable rules. When
8865                    // the rule is non-nullable and its FIRST set excludes the
8866                    // current lookahead, recursion can't find a clean path
8867                    // *through this rule*. Skipping is only safe if some sibling
8868                    // transition can still consume the lookahead — otherwise the
8869                    // rule call is the sole continuation and must run so the
8870                    // single-token insertion / deletion recovery inside the
8871                    // called rule can fire (mirroring ANTLR's reference behavior
8872                    // of conjuring a missing token at child-rule entry).
8873                    let symbol = self.token_type_at(index);
8874                    if self.fast_first_set_prefilter {
8875                        // Probe the shared cross-parse cache first; build
8876                        // the entry on miss and intern it there. The
8877                        // computation is purely a function of the ATN, so
8878                        // the cached entry is reused across parses (and
8879                        // freshly-instantiated parser values that share
8880                        // the same `&'static Atn`).
8881                        //
8882                        // `rule_first_set` returns the computed entry
8883                        // directly — it intentionally skips inserting into
8884                        // the cache when the FIRST-set walk hit a cycle, so
8885                        // we cannot assume the entry is in the cache after
8886                        // computing it.
8887                        let first = self.cached_rule_first_set(atn, target, child_stop);
8888                        if should_skip_rule_via_first_set(
8889                            &first,
8890                            symbol,
8891                            self.fast_recovery_enabled,
8892                            index,
8893                            expected,
8894                        ) {
8895                            continue;
8896                        }
8897                    }
8898                    let expected_before_child =
8899                        self.fast_recovery_enabled.then(|| expected.clone());
8900                    let mut children = self.recognize_state_fast(
8901                        atn,
8902                        FastRecognizeRequest {
8903                            state_number: target,
8904                            stop_state: child_stop,
8905                            index,
8906                            rule_start_index: index,
8907                            decision_start_index: None,
8908                            precedence: rule_precedence,
8909                            depth: depth + 1,
8910                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8911                            recovery_state: epsilon_recovery_state,
8912                        },
8913                        FastRecognizeScratch {
8914                            predicate_context,
8915                            visiting,
8916                            memo,
8917                            expected,
8918                            native_depth: native_depth + 1,
8919                        },
8920                    );
8921                    if children.is_empty() && self.fast_recovery_enabled {
8922                        children = self.fast_child_rule_failure_recovery_outcomes(
8923                            FastChildRuleFailureRecoveryRequest {
8924                                atn,
8925                                rule_index,
8926                                start_index: index,
8927                                follow_state,
8928                                stop_state,
8929                                expected,
8930                            },
8931                        );
8932                    }
8933                    if let Some(expected_before_child) = expected_before_child {
8934                        if children
8935                            .iter()
8936                            .any(|child| child.diagnostics.is_empty() && child.index > index)
8937                        {
8938                            *expected = expected_before_child;
8939                        }
8940                    }
8941                    for child in children {
8942                        let child_index = child.index;
8943                        let child_consumed_eof = child.consumed_eof;
8944                        let child_diagnostics = child.diagnostics;
8945                        let empty_recovery = self.empty_recovery_symbols();
8946                        let follow_outcomes = self.recognize_state_fast(
8947                            atn,
8948                            FastRecognizeRequest {
8949                                state_number: follow_state,
8950                                stop_state,
8951                                index: child_index,
8952                                rule_start_index,
8953                                decision_start_index: next_decision_start_index,
8954                                precedence,
8955                                depth: depth + 1,
8956                                recovery_symbols: empty_recovery,
8957                                recovery_state: None,
8958                            },
8959                            FastRecognizeScratch {
8960                                predicate_context,
8961                                visiting,
8962                                memo,
8963                                expected,
8964                                native_depth: native_depth + 1,
8965                            },
8966                        );
8967                        if follow_outcomes.is_empty() {
8968                            continue;
8969                        }
8970                        let child_stop_index =
8971                            self.rule_stop_token_index(child_index, child_consumed_eof);
8972                        let child_node = self.build_parse_trees.then(|| {
8973                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
8974                                rule_index: u32::try_from(rule_index)
8975                                    .expect("rule index fits in u32"),
8976                                invoking_state: i32::try_from(invoking_state_number(state_number))
8977                                    .expect("invoking state fits in i32"),
8978                                start_index: u32::try_from(index)
8979                                    .expect("rule start index fits in u32"),
8980                                stop_index: child_stop_index.map(|stop_index| {
8981                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
8982                                }),
8983                                deferred_children: child.deferred_nodes,
8984                                children: child.nodes,
8985                            })
8986                        });
8987                        let child_diags_empty = child_diagnostics.is_empty();
8988                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
8989                            outcome.consumed_eof |= child_consumed_eof;
8990                            // Skip the prepend dance when there's nothing to
8991                            // merge from the child — common case in pass 1.
8992                            if !child_diags_empty {
8993                                outcome.diagnostics = self
8994                                    .recognition_arena
8995                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
8996                            }
8997                            if let Some(child_node) = child_node {
8998                                outcome.deferred_nodes = self
8999                                    .recognition_arena
9000                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
9001                            }
9002                            outcome
9003                        }));
9004                    }
9005                }
9006                ParserTransitionKind::Atom
9007                | ParserTransitionKind::Range
9008                | ParserTransitionKind::Set
9009                | ParserTransitionKind::NotSet
9010                | ParserTransitionKind::Wildcard => {
9011                    #[cfg(feature = "perf-counters")]
9012                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9013                    let symbol = self.token_type_at(index);
9014                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9015                        let next_index = self.consume_index(index, symbol);
9016                        let empty_recovery = self.empty_recovery_symbols();
9017                        outcomes.extend(
9018                            self.recognize_state_fast(
9019                                atn,
9020                                FastRecognizeRequest {
9021                                    state_number: target,
9022                                    stop_state,
9023                                    index: next_index,
9024                                    rule_start_index,
9025                                    decision_start_index: next_decision_start_index,
9026                                    precedence,
9027                                    depth: depth + 1,
9028                                    recovery_symbols: empty_recovery,
9029                                    recovery_state: None,
9030                                },
9031                                FastRecognizeScratch {
9032                                    predicate_context,
9033                                    visiting,
9034                                    memo,
9035                                    expected,
9036                                    native_depth: native_depth + 1,
9037                                },
9038                            )
9039                            .into_iter()
9040                            .map(|mut outcome| {
9041                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9042                                if self.fast_token_nodes_enabled {
9043                                    let token = self.arena_token_node(index, false);
9044                                    self.defer_fast_outcome_node(&mut outcome, token);
9045                                }
9046                                outcome
9047                            }),
9048                        );
9049                    } else {
9050                        if !self.fast_recovery_enabled {
9051                            // In pass 1 there is no recovery to attempt; the
9052                            // recovery branch below would never run, and the
9053                            // `expected_symbols` computation is just there
9054                            // to gate that branch. Skipping it eliminates
9055                            // ~1× `state_expected_symbols` lookup per failed
9056                            // atom transition (≈82K on mono-statement.cs)
9057                            // for zero observable behavior change.
9058                            continue;
9059                        }
9060                        let expected_symbols = fast_recovery_expected_symbols(
9061                            self,
9062                            atn,
9063                            state.state_number(),
9064                            &recovery_symbols,
9065                        );
9066                        if expected_symbols.contains(&symbol) {
9067                            continue;
9068                        }
9069                        {
9070                            expected.record_transition(index, transition, max_token_type);
9071                            record_no_viable_if_ambiguous(
9072                                expected,
9073                                next_decision_start_index,
9074                                index,
9075                            );
9076                            outcomes.extend(self.fast_single_token_deletion_recovery(
9077                                FastRecoveryRequest {
9078                                    atn,
9079                                    transition,
9080                                    expected_symbols: Rc::clone(&expected_symbols),
9081                                    target,
9082                                    request: FastRecognizeRequest {
9083                                        state_number,
9084                                        stop_state,
9085                                        index,
9086                                        rule_start_index,
9087                                        decision_start_index,
9088                                        precedence,
9089                                        depth,
9090                                        recovery_symbols: Rc::clone(&recovery_symbols),
9091                                        recovery_state,
9092                                    },
9093                                    visiting,
9094                                    memo,
9095                                    expected,
9096                                },
9097                                predicate_context,
9098                            ));
9099                            if !state_is_left_recursive_rule(atn, state) {
9100                                outcomes.extend(self.fast_single_token_insertion_recovery(
9101                                    FastRecoveryRequest {
9102                                        atn,
9103                                        transition,
9104                                        expected_symbols: Rc::clone(&expected_symbols),
9105                                        target,
9106                                        request: FastRecognizeRequest {
9107                                            state_number,
9108                                            stop_state,
9109                                            index,
9110                                            rule_start_index,
9111                                            decision_start_index,
9112                                            precedence,
9113                                            depth,
9114                                            recovery_symbols: Rc::clone(&recovery_symbols),
9115                                            recovery_state,
9116                                        },
9117                                        visiting,
9118                                        memo,
9119                                        expected,
9120                                    },
9121                                    predicate_context,
9122                                ));
9123                            }
9124                            outcomes.extend(self.fast_current_token_deletion_recovery(
9125                                FastCurrentTokenDeletionRequest {
9126                                    atn,
9127                                    expected_symbols,
9128                                    request: FastRecognizeRequest {
9129                                        state_number,
9130                                        stop_state,
9131                                        index,
9132                                        rule_start_index,
9133                                        decision_start_index,
9134                                        precedence,
9135                                        depth,
9136                                        recovery_symbols: Rc::clone(&recovery_symbols),
9137                                        recovery_state,
9138                                    },
9139                                    visiting,
9140                                    memo,
9141                                    expected,
9142                                },
9143                                predicate_context,
9144                            ));
9145                        }
9146                    }
9147                }
9148            }
9149            let alt_number = next_alt_number(
9150                state,
9151                transition_count,
9152                transition_index,
9153                0,
9154                self.fast_track_alt_numbers,
9155            );
9156            if alt_number != 0 || left_recursive_boundary.is_some() {
9157                for outcome in &mut outcomes[outcomes_before_transition..] {
9158                    if alt_number != 0 {
9159                        self.defer_fast_outcome_alternative(outcome, alt_number);
9160                    }
9161                    if let Some(rule_index) = left_recursive_boundary {
9162                        self.defer_fast_outcome_boundary(outcome, rule_index);
9163                    }
9164                }
9165            }
9166        }
9167
9168        if has_inserted_cycle_guard {
9169            visiting.remove(&key);
9170        }
9171        if matches!(
9172            self.prediction_mode,
9173            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9174        ) && self.fast_recovery_enabled
9175        {
9176            // Without recovery enabled every outcome already has empty
9177            // diagnostics, so the discard pass is a no-op — skipping it
9178            // saves an iter+retain on each of the ~1M visits.
9179            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
9180        }
9181        if self.fast_recovery_enabled {
9182            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
9183        } else {
9184            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
9185        }
9186        // Skip memoization for single-transition states whose outcome is
9187        // unambiguous: they only get re-entered if the caller revisits the
9188        // exact same call site, which is rare since the loop above already
9189        // collapsed straight-line epsilon walks. Multi-alternative states
9190        // are where backtracking actually revisits the same coordinate, so
9191        // we still memoize there. With recovery on we keep the existing
9192        // memoization unconditionally because the recovery branch may
9193        // record diagnostics that the cache must surface to repeated
9194        // failed visits.
9195        let should_memoize = self.fast_recovery_enabled
9196            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
9197        // Apply inline pending state to each outcome before returning.
9198        // Tokens consumed inline by the loop-collapse don't appear in the
9199        // recursive recognizer's output, so we need to prepend them here.
9200        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
9201            if inline_consumed_eof {
9202                outcome.consumed_eof = true;
9203            }
9204            if !inline_consumed_tokens.is_empty() {
9205                for token_index in inline_consumed_tokens.iter().rev() {
9206                    let token = self.arena_token_node(*token_index, false);
9207                    self.defer_fast_outcome_node(&mut outcome, token);
9208                }
9209            }
9210            outcome
9211        };
9212        if should_memoize {
9213            #[cfg(feature = "perf-counters")]
9214            {
9215                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
9216                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
9217                match outcomes.len() {
9218                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9219                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9220                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9221                }
9222            }
9223            // The memo is keyed by the loop-exit `(state_number, index)` so
9224            // the inline-consumed tokens belong to *this* call's output, not
9225            // the cached result. Memoize the bare outcomes (without the
9226            // inline-pending data), then prepend the inline data on return.
9227            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
9228            memo.insert(key, Rc::clone(&stored));
9229            if inline_pending {
9230                return stored
9231                    .iter()
9232                    .copied()
9233                    .map(&mut apply_inline_pending)
9234                    .collect();
9235            }
9236            return stored.to_vec();
9237        }
9238        #[cfg(feature = "perf-counters")]
9239        match outcomes.len() {
9240            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9241            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9242            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9243        }
9244        if inline_pending {
9245            return outcomes.into_iter().map(apply_inline_pending).collect();
9246        }
9247        outcomes
9248    }
9249
9250    /// Explores single-token deletion recovery while preserving the matched
9251    /// token and skipped error token in the selected parse tree path.
9252    fn single_token_deletion_recovery(
9253        &mut self,
9254        recovery: RecoveryRequest<'_, '_>,
9255    ) -> Vec<RecognizeOutcome> {
9256        let RecoveryRequest {
9257            atn,
9258            transition,
9259            expected_symbols,
9260            target,
9261            request,
9262            visiting,
9263            memo,
9264            expected,
9265        } = recovery;
9266        let RecognizeRequest {
9267            stop_state,
9268            index,
9269            rule_start_index,
9270            decision_start_index,
9271            init_action_rules,
9272            predicates,
9273            semantics,
9274            rule_args,
9275            member_actions,
9276            return_actions,
9277            local_int_arg,
9278            member_values,
9279            return_values,
9280            rule_alt_number,
9281            track_alt_numbers,
9282            consumed_eof,
9283            precedence,
9284            depth,
9285            ..
9286        } = request;
9287        let Some((diagnostic, next_index, next_symbol)) =
9288            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
9289        else {
9290            return Vec::new();
9291        };
9292        let after_next = self.consume_index(next_index, next_symbol);
9293        self.recognize_state(
9294            atn,
9295            RecognizeRequest {
9296                state_number: target,
9297                stop_state,
9298                index: after_next,
9299                rule_start_index,
9300                decision_start_index,
9301                init_action_rules,
9302                predicates,
9303                semantics,
9304                rule_args,
9305                member_actions,
9306                return_actions,
9307                local_int_arg,
9308                member_values,
9309                return_values,
9310                rule_alt_number,
9311                track_alt_numbers,
9312                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
9313                committed_decision: false,
9314                precedence,
9315                depth: depth + 1,
9316                recovery_symbols: BTreeSet::new(),
9317                recovery_state: None,
9318            },
9319            visiting,
9320            memo,
9321            expected,
9322        )
9323        .into_iter()
9324        .map(|mut outcome| {
9325            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9326            outcome.diagnostics = self
9327                .recognition_arena
9328                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9329            let token = self.arena_token_node(next_index, false);
9330            self.arena_prepend(&mut outcome.nodes, token);
9331            let error = self.arena_token_node(index, true);
9332            self.arena_prepend(&mut outcome.nodes, error);
9333            outcome
9334        })
9335        .collect()
9336    }
9337
9338    /// Retries the current recognition state after deleting one unexpected
9339    /// token, preserving the deleted token as an error node in the parse tree.
9340    fn current_token_deletion_recovery(
9341        &mut self,
9342        recovery: CurrentTokenDeletionRequest<'_, '_>,
9343    ) -> Vec<RecognizeOutcome> {
9344        let CurrentTokenDeletionRequest {
9345            atn,
9346            expected_symbols,
9347            mut request,
9348            visiting,
9349            memo,
9350            expected,
9351        } = recovery;
9352        let error_index = request.index;
9353        if error_index == request.rule_start_index {
9354            return Vec::new();
9355        }
9356        let Some((diagnostic, next_index, skipped)) =
9357            self.current_token_deletion(error_index, &expected_symbols)
9358        else {
9359            return Vec::new();
9360        };
9361        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9362        request.index = next_index;
9363        request.committed_decision = false;
9364        request.depth += 1;
9365        request.recovery_state = None;
9366        self.recognize_state(atn, request, visiting, memo, expected)
9367            .into_iter()
9368            .map(|mut outcome| {
9369                outcome.diagnostics = self
9370                    .recognition_arena
9371                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9372                for index in skipped.iter().rev() {
9373                    let error = self.arena_token_node(*index, true);
9374                    self.arena_prepend(&mut outcome.nodes, error);
9375                }
9376                outcome
9377            })
9378            .collect()
9379    }
9380
9381    /// Falls back after deletion/insertion repairs cannot continue from a
9382    /// failed consuming transition.
9383    fn consuming_failure_fallback(
9384        &mut self,
9385        fallback: ConsumingFailureFallback<'_>,
9386        visiting: &mut BTreeSet<RecognizeKey>,
9387        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9388        expected: &mut ExpectedTokens,
9389    ) -> Vec<RecognizeOutcome> {
9390        if fallback.expected_symbols.is_empty() {
9391            return Vec::new();
9392        }
9393        if fallback.symbol == TOKEN_EOF {
9394            return self.eof_consuming_failure_fallback(fallback, expected);
9395        }
9396        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9397    }
9398
9399    /// Keeps unexpected non-EOF input visible as an error node when no repair
9400    /// path can otherwise reach the transition target.
9401    fn non_eof_consuming_failure_fallback(
9402        &mut self,
9403        fallback: ConsumingFailureFallback<'_>,
9404        visiting: &mut BTreeSet<RecognizeKey>,
9405        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9406        expected: &mut ExpectedTokens,
9407    ) -> Vec<RecognizeOutcome> {
9408        let ConsumingFailureFallback {
9409            atn,
9410            target,
9411            request,
9412            symbol,
9413            expected_symbols,
9414            decision_start_index,
9415            decision,
9416        } = fallback;
9417        let error_index = request.index;
9418        let diagnostic =
9419            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
9420        let next_index = self.consume_index(error_index, symbol);
9421        self.recognize_state(
9422            atn,
9423            RecognizeRequest {
9424                state_number: target,
9425                stop_state: request.stop_state,
9426                index: next_index,
9427                rule_start_index: request.rule_start_index,
9428                decision_start_index,
9429                init_action_rules: request.init_action_rules,
9430                predicates: request.predicates,
9431                semantics: request.semantics,
9432                rule_args: request.rule_args,
9433                member_actions: request.member_actions,
9434                return_actions: request.return_actions,
9435                local_int_arg: request.local_int_arg,
9436                member_values: request.member_values,
9437                return_values: request.return_values,
9438                rule_alt_number: request.rule_alt_number,
9439                track_alt_numbers: request.track_alt_numbers,
9440                consumed_eof: request.consumed_eof,
9441                committed_decision: false,
9442                precedence: request.precedence,
9443                depth: request.depth + 1,
9444                recovery_symbols: BTreeSet::new(),
9445                recovery_state: None,
9446            },
9447            visiting,
9448            memo,
9449            expected,
9450        )
9451        .into_iter()
9452        .map(|mut outcome| {
9453            prepend_decision(&mut outcome, decision);
9454            outcome.diagnostics = self
9455                .recognition_arena
9456                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9457            let error = self.arena_token_node(error_index, true);
9458            self.arena_prepend(&mut outcome.nodes, error);
9459            outcome
9460        })
9461        .collect()
9462    }
9463
9464    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
9465    /// behavior of unwinding instead of inserting caller tokens at EOF.
9466    fn eof_consuming_failure_fallback(
9467        &mut self,
9468        fallback: ConsumingFailureFallback<'_>,
9469        expected: &ExpectedTokens,
9470    ) -> Vec<RecognizeOutcome> {
9471        let request = fallback.request;
9472        if request.index == request.rule_start_index {
9473            return Vec::new();
9474        }
9475        let diagnostic =
9476            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
9477        let diagnostics = self
9478            .recognition_arena
9479            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
9480        vec![RecognizeOutcome {
9481            index: request.index,
9482            consumed_eof: request.consumed_eof,
9483            alt_number: request.rule_alt_number,
9484            member_values: request.member_values,
9485            return_values: request.return_values,
9486            diagnostics,
9487            decisions: Vec::new(),
9488            actions: Vec::new(),
9489            nodes: NodeSeqId::EMPTY,
9490        }]
9491    }
9492
9493    /// Explores single-token insertion recovery while adding a conjured
9494    /// missing-token error node to the selected parse tree path.
9495    fn single_token_insertion_recovery(
9496        &mut self,
9497        recovery: RecoveryRequest<'_, '_>,
9498    ) -> Vec<RecognizeOutcome> {
9499        let RecoveryRequest {
9500            atn,
9501            transition,
9502            expected_symbols,
9503            target,
9504            request,
9505            visiting,
9506            memo,
9507            expected,
9508        } = recovery;
9509        let RecognizeRequest {
9510            stop_state,
9511            index,
9512            rule_start_index,
9513            decision_start_index,
9514            init_action_rules,
9515            predicates,
9516            semantics,
9517            rule_args,
9518            member_actions,
9519            return_actions,
9520            local_int_arg,
9521            member_values,
9522            return_values,
9523            rule_alt_number,
9524            track_alt_numbers,
9525            consumed_eof,
9526            precedence,
9527            depth,
9528            ..
9529        } = request;
9530        let follow_symbols = state_expected_symbols(atn, transition.target());
9531        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
9532            transition,
9533            index,
9534            atn.max_token_type(),
9535            &expected_symbols,
9536            &follow_symbols,
9537        ) else {
9538            return Vec::new();
9539        };
9540        self.recognize_state(
9541            atn,
9542            RecognizeRequest {
9543                state_number: target,
9544                stop_state,
9545                index,
9546                rule_start_index,
9547                decision_start_index,
9548                init_action_rules,
9549                predicates,
9550                semantics,
9551                rule_args,
9552                member_actions,
9553                return_actions,
9554                local_int_arg,
9555                member_values,
9556                return_values,
9557                rule_alt_number,
9558                track_alt_numbers,
9559                consumed_eof,
9560                committed_decision: false,
9561                precedence,
9562                depth: depth + 1,
9563                recovery_symbols: BTreeSet::new(),
9564                recovery_state: None,
9565            },
9566            visiting,
9567            memo,
9568            expected,
9569        )
9570        .into_iter()
9571        .map(|mut outcome| {
9572            outcome.diagnostics = self
9573                .recognition_arena
9574                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9575            let missing = self.arena_missing_token_node(token_type, index, text.clone());
9576            self.arena_prepend(&mut outcome.nodes, missing);
9577            outcome
9578        })
9579        .collect()
9580    }
9581
9582    /// Attempts to reach `stop_state` and carries semantic actions for the
9583    /// selected parser path.
9584    #[allow(clippy::too_many_lines)]
9585    fn recognize_state(
9586        &mut self,
9587        atn: &Atn,
9588        request: RecognizeRequest<'_>,
9589        visiting: &mut BTreeSet<RecognizeKey>,
9590        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9591        expected: &mut ExpectedTokens,
9592    ) -> Vec<RecognizeOutcome> {
9593        let request_template = request.clone();
9594        let RecognizeRequest {
9595            state_number,
9596            stop_state,
9597            index,
9598            rule_start_index,
9599            decision_start_index,
9600            init_action_rules,
9601            predicates,
9602            semantics,
9603            rule_args,
9604            member_actions,
9605            return_actions,
9606            local_int_arg,
9607            member_values,
9608            return_values,
9609            rule_alt_number,
9610            track_alt_numbers,
9611            consumed_eof,
9612            committed_decision,
9613            precedence,
9614            depth,
9615            recovery_symbols,
9616            recovery_state,
9617        } = request;
9618        if depth > RECOGNITION_DEPTH_LIMIT {
9619            return Vec::new();
9620        }
9621        if state_number == stop_state {
9622            return stop_outcome(
9623                index,
9624                consumed_eof,
9625                rule_alt_number,
9626                member_values,
9627                return_values,
9628            );
9629        }
9630        let key = RecognizeKey {
9631            state_number,
9632            stop_state,
9633            index,
9634            rule_start_index,
9635            decision_start_index,
9636            local_int_arg,
9637            member_values: member_values.clone(),
9638            return_values: return_values.clone(),
9639            rule_alt_number,
9640            track_alt_numbers,
9641            consumed_eof,
9642            committed_decision,
9643            precedence,
9644            recovery_symbols: recovery_symbols.clone(),
9645            recovery_state,
9646        };
9647        if let Some(outcomes) = memo.get(&key) {
9648            return outcomes.clone();
9649        }
9650
9651        let visit_key = key.clone();
9652        if !visiting.insert(visit_key.clone()) {
9653            return Vec::new();
9654        }
9655
9656        let Some(state) = atn.state(state_number) else {
9657            visiting.remove(&visit_key);
9658            return Vec::new();
9659        };
9660        let decision_override_generation = self.decision_override_generation;
9661        let transitions = state.transitions();
9662        let transition_count = transitions.len();
9663        let overridden_transition = if transition_count > 1
9664            && self.semantic_hooks.observes_parser_decisions()
9665        {
9666            atn.decision_to_state()
9667                .iter()
9668                .position(|candidate| candidate == state_number)
9669                .and_then(|decision| {
9670                    self.semantic_hooks
9671                        .parser_decision_override(decision, index, transition_count)
9672                })
9673                .and_then(|alternative| alternative.checked_sub(1))
9674                .filter(|alternative| *alternative < transition_count)
9675        } else {
9676            None
9677        };
9678        if overridden_transition.is_some() {
9679            self.decision_override_generation = self.decision_override_generation.wrapping_add(1);
9680        }
9681        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9682            Some(index)
9683        } else {
9684            decision_start_index
9685        };
9686        let (epsilon_recovery_symbols, epsilon_recovery_state) =
9687            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
9688        let mut outcomes = Vec::new();
9689        for (transition_index, transition) in transitions.iter().enumerate() {
9690            if overridden_transition.is_some_and(|forced| forced != transition_index) {
9691                continue;
9692            }
9693            let transition_committed =
9694                committed_decision || overridden_transition == Some(transition_index);
9695            let mut transition_request = request_template.clone();
9696            transition_request.committed_decision = transition_committed;
9697            let decision =
9698                transition_decision(atn, state, transition_count, transition_index, predicates);
9699            let next_alt_number = next_alt_number(
9700                state,
9701                transition_count,
9702                transition_index,
9703                rule_alt_number,
9704                track_alt_numbers,
9705            );
9706            let transition_data = transition.data();
9707            match &transition_data {
9708                Transition::Epsilon { target } | Transition::Action { target, .. } => {
9709                    let action_rule_index = match &transition_data {
9710                        Transition::Action { rule_index, .. } => Some(*rule_index),
9711                        _ => None,
9712                    };
9713                    outcomes.extend(self.recognize_epsilon_or_action_step(
9714                        atn,
9715                        &transition_request,
9716                        EpsilonActionStep {
9717                            source_state: state_number,
9718                            target: *target,
9719                            action_rule_index,
9720                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
9721                            decision,
9722                            decision_start_index: next_decision_start_index,
9723                            alt_number: next_alt_number,
9724                            recovery_symbols: epsilon_recovery_symbols.clone(),
9725                            recovery_state: epsilon_recovery_state,
9726                        },
9727                        RecognizeScratch {
9728                            visiting,
9729                            memo,
9730                            expected,
9731                        },
9732                    ));
9733                }
9734                Transition::Predicate {
9735                    target,
9736                    rule_index,
9737                    pred_index,
9738                    ..
9739                } => {
9740                    let predicate = PredicateEval {
9741                        index,
9742                        rule_index: *rule_index,
9743                        pred_index: *pred_index,
9744                        predicates,
9745                        semantics,
9746                        context: None,
9747                        local_int_arg,
9748                        member_values: &member_values,
9749                    };
9750                    if self.parser_predicate_matches(predicate) {
9751                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
9752                        outcomes.extend(
9753                            self.recognize_state(
9754                                atn,
9755                                RecognizeRequest {
9756                                    state_number: *target,
9757                                    stop_state,
9758                                    index,
9759                                    rule_start_index,
9760                                    decision_start_index: next_decision_start_index,
9761                                    init_action_rules,
9762                                    predicates,
9763                                    semantics,
9764                                    rule_args,
9765                                    member_actions,
9766                                    return_actions,
9767                                    local_int_arg,
9768                                    member_values: member_values.clone(),
9769                                    return_values: return_values.clone(),
9770                                    rule_alt_number: next_alt_number,
9771                                    track_alt_numbers,
9772                                    consumed_eof,
9773                                    committed_decision: transition_committed,
9774                                    precedence,
9775                                    depth: depth + 1,
9776                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9777                                    recovery_state: epsilon_recovery_state,
9778                                },
9779                                visiting,
9780                                memo,
9781                                expected,
9782                            )
9783                            .into_iter()
9784                            .map(|mut outcome| {
9785                                prepend_decision(&mut outcome, decision);
9786                                if let Some(rule_index) = left_recursive_boundary {
9787                                    let boundary =
9788                                        self.arena_boundary_node(rule_index, next_alt_number);
9789                                    self.arena_prepend(&mut outcome.nodes, boundary);
9790                                }
9791                                outcome
9792                            }),
9793                        );
9794                    } else if let Some(message) = semantics
9795                        .and_then(|semantics| {
9796                            self.parser_semantic_ir_predicate_failure_message(
9797                                *rule_index,
9798                                *pred_index,
9799                                semantics,
9800                            )
9801                        })
9802                        .or_else(|| {
9803                            self.parser_predicate_failure_message(
9804                                *rule_index,
9805                                *pred_index,
9806                                predicates,
9807                            )
9808                        })
9809                    {
9810                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
9811                            rule_index: *rule_index,
9812                            index,
9813                            message,
9814                            member_values: member_values.clone(),
9815                            return_values: return_values.clone(),
9816                            rule_alt_number,
9817                        }));
9818                    } else {
9819                        record_predicate_no_viable(expected, next_decision_start_index, index);
9820                    }
9821                }
9822                Transition::Precedence {
9823                    target,
9824                    precedence: transition_precedence,
9825                } => {
9826                    if *transition_precedence >= precedence {
9827                        outcomes.extend(
9828                            self.recognize_state(
9829                                atn,
9830                                RecognizeRequest {
9831                                    state_number: *target,
9832                                    stop_state,
9833                                    index,
9834                                    rule_start_index,
9835                                    decision_start_index: next_decision_start_index,
9836                                    init_action_rules,
9837                                    predicates,
9838                                    semantics,
9839                                    rule_args,
9840                                    member_actions,
9841                                    return_actions,
9842                                    local_int_arg,
9843                                    member_values: member_values.clone(),
9844                                    return_values: return_values.clone(),
9845                                    rule_alt_number: next_alt_number,
9846                                    track_alt_numbers,
9847                                    consumed_eof,
9848                                    committed_decision: transition_committed,
9849                                    precedence,
9850                                    depth: depth + 1,
9851                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9852                                    recovery_state: epsilon_recovery_state,
9853                                },
9854                                visiting,
9855                                memo,
9856                                expected,
9857                            )
9858                            .into_iter()
9859                            .map(|mut outcome| {
9860                                prepend_decision(&mut outcome, decision);
9861                                outcome
9862                            }),
9863                        );
9864                    }
9865                }
9866                Transition::Rule {
9867                    target,
9868                    rule_index,
9869                    follow_state,
9870                    precedence: rule_precedence,
9871                    ..
9872                } => {
9873                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
9874                        continue;
9875                    };
9876                    let child_local_int_arg =
9877                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
9878                    let expected_before_child = expected.clone();
9879                    let children = self.recognize_state(
9880                        atn,
9881                        RecognizeRequest {
9882                            state_number: *target,
9883                            stop_state: child_stop,
9884                            index,
9885                            rule_start_index: index,
9886                            decision_start_index: None,
9887                            init_action_rules,
9888                            predicates,
9889                            semantics,
9890                            rule_args,
9891                            member_actions,
9892                            return_actions,
9893                            local_int_arg: child_local_int_arg,
9894                            member_values: member_values.clone(),
9895                            return_values: BTreeMap::new(),
9896                            rule_alt_number: 0,
9897                            track_alt_numbers,
9898                            consumed_eof: false,
9899                            committed_decision: transition_committed,
9900                            precedence: *rule_precedence,
9901                            depth: depth + 1,
9902                            recovery_symbols: epsilon_recovery_symbols.clone(),
9903                            recovery_state: epsilon_recovery_state,
9904                        },
9905                        visiting,
9906                        memo,
9907                        expected,
9908                    );
9909                    let children = if children.is_empty() {
9910                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
9911                            atn,
9912                            rule_index: *rule_index,
9913                            start_index: index,
9914                            follow_state: *follow_state,
9915                            stop_state,
9916                            member_values: member_values.clone(),
9917                            expected,
9918                        })
9919                    } else {
9920                        children
9921                    };
9922                    let preserve_child_expected =
9923                        self.child_expected_reaches_clean_eof(&children, expected);
9924                    restore_expected(
9925                        &children,
9926                        index,
9927                        expected,
9928                        expected_before_child,
9929                        preserve_child_expected,
9930                    );
9931                    for child in children {
9932                        let child_stop_index =
9933                            self.rule_stop_token_index(child.index, child.consumed_eof);
9934                        let child_nodes = self
9935                            .recognition_arena
9936                            .fold_left_recursive_boundaries(child.nodes);
9937                        let child_node = self.arena_rule_node(ArenaRuleSpec {
9938                            rule_index: *rule_index,
9939                            invoking_state: invoking_state_number(state_number),
9940                            alt_number: child.alt_number,
9941                            start_index: index,
9942                            stop_index: child_stop_index,
9943                            return_values: child.return_values.clone(),
9944                            children: child_nodes,
9945                        });
9946                        outcomes.extend(
9947                            self.recognize_state(
9948                                atn,
9949                                RecognizeRequest {
9950                                    state_number: *follow_state,
9951                                    stop_state,
9952                                    index: child.index,
9953                                    rule_start_index,
9954                                    decision_start_index: next_decision_start_index,
9955                                    init_action_rules,
9956                                    predicates,
9957                                    semantics,
9958                                    rule_args,
9959                                    member_actions,
9960                                    return_actions,
9961                                    local_int_arg,
9962                                    member_values: child.member_values.clone(),
9963                                    return_values: return_values.clone(),
9964                                    rule_alt_number,
9965                                    track_alt_numbers,
9966                                    consumed_eof: consumed_eof || child.consumed_eof,
9967                                    committed_decision: transition_committed
9968                                        && child.index == index,
9969                                    precedence,
9970                                    depth: depth + 1,
9971                                    recovery_symbols: BTreeSet::new(),
9972                                    recovery_state: None,
9973                                },
9974                                visiting,
9975                                memo,
9976                                expected,
9977                            )
9978                            .into_iter()
9979                            .map(|mut outcome| {
9980                                outcome.consumed_eof |= child.consumed_eof;
9981                                outcome.diagnostics = self
9982                                    .recognition_arena
9983                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
9984                                let mut decisions = child.decisions.clone();
9985                                decisions.append(&mut outcome.decisions);
9986                                outcome.decisions = decisions;
9987                                prepend_decision(&mut outcome, decision);
9988                                let mut actions = child.actions.clone();
9989                                if init_action_rules.contains(rule_index) {
9990                                    actions.insert(
9991                                        0,
9992                                        ParserAction::new_rule_init(
9993                                            *rule_index,
9994                                            index,
9995                                            Some(*follow_state),
9996                                        ),
9997                                    );
9998                                }
9999                                actions.append(&mut outcome.actions);
10000                                outcome.actions = actions;
10001                                self.arena_prepend(&mut outcome.nodes, child_node);
10002                                outcome
10003                            }),
10004                        );
10005                    }
10006                }
10007                Transition::Atom { target, .. }
10008                | Transition::Range { target, .. }
10009                | Transition::Set { target, .. }
10010                | Transition::NotSet { target, .. }
10011                | Transition::Wildcard { target, .. } => {
10012                    let symbol = self.token_type_at(index);
10013                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
10014                        let next_index = self.consume_index(index, symbol);
10015                        outcomes.extend(
10016                            self.recognize_state(
10017                                atn,
10018                                RecognizeRequest {
10019                                    state_number: *target,
10020                                    stop_state,
10021                                    index: next_index,
10022                                    rule_start_index,
10023                                    decision_start_index: next_decision_start_index,
10024                                    init_action_rules,
10025                                    predicates,
10026                                    semantics,
10027                                    rule_args,
10028                                    member_actions,
10029                                    return_actions,
10030                                    local_int_arg,
10031                                    member_values: member_values.clone(),
10032                                    return_values: return_values.clone(),
10033                                    rule_alt_number: next_alt_number,
10034                                    track_alt_numbers,
10035                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
10036                                    committed_decision: false,
10037                                    precedence,
10038                                    depth: depth + 1,
10039                                    recovery_symbols: BTreeSet::new(),
10040                                    recovery_state: None,
10041                                },
10042                                visiting,
10043                                memo,
10044                                expected,
10045                            )
10046                            .into_iter()
10047                            .map(|mut outcome| {
10048                                prepend_decision(&mut outcome, decision);
10049                                outcome.consumed_eof |= symbol == TOKEN_EOF;
10050                                let token = self.arena_token_node(index, false);
10051                                self.arena_prepend(&mut outcome.nodes, token);
10052                                outcome
10053                            }),
10054                        );
10055                    } else {
10056                        let expected_symbols =
10057                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
10058                        if expected_symbols.contains(&symbol) && !transition_committed {
10059                            continue;
10060                        }
10061                        expected.record_transition(index, transition, atn.max_token_type());
10062                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
10063                        let before_recovery = outcomes.len();
10064                        let recovery_request = transition_request.clone();
10065                        if transition_committed {
10066                            outcomes.extend(self.consuming_failure_fallback(
10067                                ConsumingFailureFallback {
10068                                    atn,
10069                                    target: *target,
10070                                    request: recovery_request,
10071                                    symbol,
10072                                    expected_symbols,
10073                                    decision_start_index: next_decision_start_index,
10074                                    decision,
10075                                },
10076                                visiting,
10077                                memo,
10078                                expected,
10079                            ));
10080                            break;
10081                        }
10082                        outcomes.extend(
10083                            self.single_token_deletion_recovery(RecoveryRequest {
10084                                atn,
10085                                transition,
10086                                expected_symbols: expected_symbols.clone(),
10087                                target: *target,
10088                                request: recovery_request.clone(),
10089                                visiting,
10090                                memo,
10091                                expected,
10092                            })
10093                            .into_iter()
10094                            .map(|mut outcome| {
10095                                prepend_decision(&mut outcome, decision);
10096                                outcome
10097                            }),
10098                        );
10099                        if !state_is_left_recursive_rule(atn, state) {
10100                            outcomes.extend(
10101                                self.single_token_insertion_recovery(RecoveryRequest {
10102                                    atn,
10103                                    transition,
10104                                    expected_symbols: expected_symbols.clone(),
10105                                    target: *target,
10106                                    request: recovery_request.clone(),
10107                                    visiting,
10108                                    memo,
10109                                    expected,
10110                                })
10111                                .into_iter()
10112                                .map(|mut outcome| {
10113                                    prepend_decision(&mut outcome, decision);
10114                                    outcome
10115                                }),
10116                            );
10117                        }
10118                        outcomes.extend(self.current_token_deletion_recovery(
10119                            CurrentTokenDeletionRequest {
10120                                atn,
10121                                expected_symbols: expected_symbols.clone(),
10122                                request: recovery_request.clone(),
10123                                visiting,
10124                                memo,
10125                                expected,
10126                            },
10127                        ));
10128                        if outcomes.len() == before_recovery {
10129                            outcomes.extend(self.consuming_failure_fallback(
10130                                ConsumingFailureFallback {
10131                                    atn,
10132                                    target: *target,
10133                                    request: recovery_request,
10134                                    symbol,
10135                                    expected_symbols,
10136                                    decision_start_index: next_decision_start_index,
10137                                    decision,
10138                                },
10139                                visiting,
10140                                memo,
10141                                expected,
10142                            ));
10143                        }
10144                    }
10145                }
10146            }
10147            if self.decision_override_generation != decision_override_generation {
10148                break;
10149            }
10150        }
10151
10152        visiting.remove(&visit_key);
10153        self.record_prediction_diagnostics(atn, state, index, &outcomes);
10154        if matches!(
10155            self.prediction_mode,
10156            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
10157        ) {
10158            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
10159        }
10160        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
10161        memo.insert(key, outcomes.clone());
10162        outcomes
10163    }
10164
10165    /// Follows an epsilon or semantic-action transition while preserving the
10166    /// path-local side effects that may later become generated action output.
10167    fn recognize_epsilon_or_action_step(
10168        &mut self,
10169        atn: &Atn,
10170        request: &RecognizeRequest<'_>,
10171        step: EpsilonActionStep,
10172        scratch: RecognizeScratch<'_>,
10173    ) -> Vec<RecognizeOutcome> {
10174        let RecognizeScratch {
10175            visiting,
10176            memo,
10177            expected,
10178        } = scratch;
10179        let action = step.action_rule_index.map(|rule_index| {
10180            ParserAction::new(
10181                step.source_state,
10182                rule_index,
10183                request.rule_start_index,
10184                self.rule_stop_token_index(request.index, request.consumed_eof),
10185            )
10186        });
10187        let next_member_values = if action.is_some() {
10188            member_values_after_action(
10189                step.source_state,
10190                request.member_actions,
10191                request.semantics,
10192                &request.member_values,
10193            )
10194        } else {
10195            request.member_values.clone()
10196        };
10197        let next_return_values = action.map_or_else(
10198            || request.return_values.clone(),
10199            |action| {
10200                return_values_after_action(
10201                    step.source_state,
10202                    action.rule_index(),
10203                    request.return_actions,
10204                    request.semantics,
10205                    &request.return_values,
10206                )
10207            },
10208        );
10209
10210        self.recognize_state(
10211            atn,
10212            RecognizeRequest {
10213                state_number: step.target,
10214                stop_state: request.stop_state,
10215                index: request.index,
10216                rule_start_index: request.rule_start_index,
10217                decision_start_index: step.decision_start_index,
10218                init_action_rules: request.init_action_rules,
10219                predicates: request.predicates,
10220                semantics: request.semantics,
10221                rule_args: request.rule_args,
10222                member_actions: request.member_actions,
10223                return_actions: request.return_actions,
10224                local_int_arg: request.local_int_arg,
10225                member_values: next_member_values,
10226                return_values: next_return_values,
10227                rule_alt_number: if step.left_recursive_boundary.is_some() {
10228                    0
10229                } else {
10230                    step.alt_number
10231                },
10232                track_alt_numbers: request.track_alt_numbers,
10233                consumed_eof: request.consumed_eof,
10234                committed_decision: request.committed_decision,
10235                precedence: request.precedence,
10236                depth: request.depth + 1,
10237                recovery_symbols: step.recovery_symbols,
10238                recovery_state: step.recovery_state,
10239            },
10240            visiting,
10241            memo,
10242            expected,
10243        )
10244        .into_iter()
10245        .map(|mut outcome| {
10246            prepend_decision(&mut outcome, step.decision);
10247            if let Some(rule_index) = step.left_recursive_boundary {
10248                let boundary = self.arena_boundary_node(rule_index, step.alt_number);
10249                self.arena_prepend(&mut outcome.nodes, boundary);
10250            }
10251            if let Some(action) = action {
10252                outcome.actions.insert(0, action);
10253            }
10254            outcome
10255        })
10256        .collect()
10257    }
10258
10259    /// Reads the token type at an absolute token-stream index without moving
10260    /// the parser's stream cursor. The fast recognizer probes lookahead at
10261    /// every state visit, so avoiding the seek round-trip is a measurable
10262    /// hot-path win on long inputs.
10263    fn token_type_at(&mut self, index: usize) -> i32 {
10264        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
10265            self.input.fill();
10266        }
10267        self.input.token_type_at_index(index)
10268    }
10269
10270    /// Returns the cached `state_expected_symbols` set for an ATN state.
10271    ///
10272    /// The fast recognizer consults this set on every state visit through
10273    /// `next_recovery_context`; the underlying DFS is a pure function of the
10274    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
10275    ///
10276    /// Caching is layered through `intern_recovery_symbols` so two ATN states
10277    /// with the same expected-symbol set share one `Rc`. That invariant is
10278    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
10279    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
10280    /// always interned before it ends up in a key.
10281    fn cached_state_expected_symbols(
10282        &mut self,
10283        atn: &Atn,
10284        state_number: usize,
10285    ) -> Rc<BTreeSet<i32>> {
10286        if let Some(cached) = self.state_expected_cache.get(&state_number) {
10287            return Rc::clone(cached);
10288        }
10289        let symbols = state_expected_symbols(atn, state_number);
10290        let entry = self.intern_recovery_symbols(symbols);
10291        self.state_expected_cache
10292            .insert(state_number, Rc::clone(&entry));
10293        entry
10294    }
10295
10296    fn cached_state_expected_token_set(
10297        &mut self,
10298        atn: &Atn,
10299        state_number: usize,
10300    ) -> Rc<TokenBitSet> {
10301        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
10302            return Rc::clone(cached);
10303        }
10304        // Purely a function of the ATN, so back the per-parser cache with the
10305        // thread-shared one — fresh parser instances (one per parse in
10306        // generated usage) start warm instead of rewalking the ATN.
10307        let symbols = with_shared_atn_caches(atn, |cache| {
10308            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
10309                return Rc::clone(cached);
10310            }
10311            let symbols = Rc::new(state_expected_token_set(atn, state_number));
10312            cache
10313                .state_expected_tokens
10314                .insert(state_number, Rc::clone(&symbols));
10315            symbols
10316        });
10317        self.state_expected_token_cache
10318            .insert(state_number, Rc::clone(&symbols));
10319        symbols
10320    }
10321
10322    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
10323        if self.rule_stop_reach_cache.len() <= state_number {
10324            self.rule_stop_reach_cache
10325                .resize_with(atn.states().len().max(state_number + 1), || None);
10326        }
10327        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
10328            return reaches;
10329        }
10330        let reaches = with_shared_atn_caches(atn, |cache| {
10331            *cache
10332                .rule_stop_reach
10333                .entry(state_number)
10334                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
10335        });
10336        self.rule_stop_reach_cache[state_number] = Some(reaches);
10337        reaches
10338    }
10339
10340    /// Returns the parser's empty `recovery_symbols` singleton so callers can
10341    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
10342    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
10343        Rc::clone(&self.empty_recovery_symbols)
10344    }
10345
10346    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
10347    /// recognizer can hash and compare keys by pointer.
10348    ///
10349    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
10350    /// come from this method or the empty singleton; otherwise two
10351    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
10352    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
10353    /// recognition coordinates.
10354    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
10355        if set.is_empty() {
10356            return Rc::clone(&self.empty_recovery_symbols);
10357        }
10358        let candidate = Rc::new(set);
10359        match self.recovery_symbols_intern.get(&candidate) {
10360            Some(existing) => Rc::clone(existing),
10361            None => {
10362                self.recovery_symbols_intern
10363                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
10364                candidate
10365            }
10366        }
10367    }
10368
10369    /// Returns the cached look-1 entry for a decision state, computing it on
10370    /// first use. Multi-alternative states are visited many times during
10371    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
10372    /// hash lookup per visit.
10373    fn cached_decision_lookahead(
10374        &mut self,
10375        atn: &Atn,
10376        state: AtnState<'_>,
10377        rule_stop_state: usize,
10378    ) -> Rc<DecisionLookahead> {
10379        // Hit the parser-instance cache first. Decision lookahead is purely
10380        // a function of the ATN/state, so on a warm cache we skip the
10381        // thread-local + RefCell + HashMap-entry dance through
10382        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
10383        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
10384        // hashmap-entry overhead in profiles.
10385        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
10386            return Rc::clone(cached);
10387        }
10388        let entry = with_shared_atn_caches(atn, |cache| {
10389            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10390                return Rc::clone(cached);
10391            }
10392            let mut entry = DecisionLookahead {
10393                transitions: Vec::with_capacity(state.transitions().len()),
10394            };
10395            for transition in &state.transitions() {
10396                entry.transitions.push(transition_first_set(
10397                    atn,
10398                    transition,
10399                    rule_stop_state,
10400                    &mut cache.first_set,
10401                ));
10402            }
10403            let entry = Rc::new(entry);
10404            cache
10405                .decision_lookahead
10406                .insert(state.state_number(), Rc::clone(&entry));
10407            entry
10408        });
10409        self.decision_lookahead_cache
10410            .insert(state.state_number(), Rc::clone(&entry));
10411        entry
10412    }
10413
10414    fn cached_rule_first_set(
10415        &mut self,
10416        atn: &Atn,
10417        target: usize,
10418        child_stop: usize,
10419    ) -> Rc<FirstSet> {
10420        if self.rule_first_set_cache.len() <= target {
10421            self.rule_first_set_cache
10422                .resize_with(atn.states().len().max(target + 1), || None);
10423        }
10424        if let Some(cached) = self
10425            .rule_first_set_cache
10426            .get(target)
10427            .and_then(Option::as_ref)
10428        {
10429            return Rc::clone(cached);
10430        }
10431        let first = with_shared_first_set_cache(atn, |cache| {
10432            rule_first_set(atn, target, child_stop, cache)
10433        });
10434        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
10435        first
10436    }
10437
10438    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
10439        let atn_key = SharedAtnCacheKey::for_atn(atn);
10440        if self.empty_cycle_cache_atn != Some(atn_key) {
10441            self.empty_cycle_cache.clear();
10442            self.empty_cycle_cache_atn = Some(atn_key);
10443        }
10444        if self.empty_cycle_cache.len() <= state_number {
10445            self.empty_cycle_cache
10446                .resize_with(atn.state_count().max(state_number + 1), || None);
10447        }
10448        if let Some(cached) = self.empty_cycle_cache[state_number] {
10449            return cached;
10450        }
10451        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
10452        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
10453        self.empty_cycle_cache[state_number] = Some(result);
10454        result
10455    }
10456
10457    fn empty_path_reaches_state(
10458        &mut self,
10459        atn: &Atn,
10460        state_number: usize,
10461        target_state: usize,
10462        visited: &mut FxHashSet<usize>,
10463    ) -> bool {
10464        enum Work {
10465            Visit(usize),
10466            RuleFollow {
10467                target: usize,
10468                rule_index: usize,
10469                follow_state: usize,
10470            },
10471        }
10472
10473        let mut work = vec![Work::Visit(state_number)];
10474        while let Some(item) = work.pop() {
10475            match item {
10476                Work::Visit(state_number) => {
10477                    if !visited.insert(state_number) {
10478                        continue;
10479                    }
10480                    let Some(state) = atn.state(state_number) else {
10481                        continue;
10482                    };
10483                    let transitions = state.transitions();
10484                    for transition_index in (0..transitions.len()).rev() {
10485                        let transition = transitions
10486                            .get(transition_index)
10487                            .expect("in-bounds parser transition");
10488                        let kind = transition.kind();
10489                        let target = transition.target();
10490                        match kind {
10491                            ParserTransitionKind::Atom
10492                            | ParserTransitionKind::Range
10493                            | ParserTransitionKind::Set
10494                            | ParserTransitionKind::NotSet
10495                            | ParserTransitionKind::Wildcard => {}
10496                            ParserTransitionKind::Rule => {
10497                                if target == target_state {
10498                                    return true;
10499                                }
10500                                work.push(Work::RuleFollow {
10501                                    target,
10502                                    rule_index: transition.arg0() as usize,
10503                                    follow_state: transition.arg1() as usize,
10504                                });
10505                                work.push(Work::Visit(target));
10506                            }
10507                            ParserTransitionKind::Epsilon
10508                            | ParserTransitionKind::Predicate
10509                            | ParserTransitionKind::Action
10510                            | ParserTransitionKind::Precedence => {
10511                                if target == target_state {
10512                                    return true;
10513                                }
10514                                work.push(Work::Visit(target));
10515                            }
10516                        }
10517                    }
10518                }
10519                Work::RuleFollow {
10520                    target,
10521                    rule_index,
10522                    follow_state,
10523                } => {
10524                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
10525                        continue;
10526                    };
10527                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
10528                        if follow_state == target_state {
10529                            return true;
10530                        }
10531                        work.push(Work::Visit(follow_state));
10532                    }
10533                }
10534            }
10535        }
10536        false
10537    }
10538
10539    /// Decides whether the clean recognizer should use its full outcome memo
10540    /// table for this coordinate.
10541    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
10542        match self.clean_memo_mode {
10543            CleanMemoMode::Promote => true,
10544            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
10545            CleanMemoMode::Sparse => {
10546                self.clean_memo_sparse_samples += 1;
10547                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
10548                    return false;
10549                }
10550                self.clean_memo_sparse_samples = 0;
10551                self.clean_memo_mode = CleanMemoMode::Probe;
10552                self.clean_memo_probe_samples = 0;
10553                self.clean_memo_probe_repeats = 0;
10554                self.clean_memo_probe_seen.clear();
10555                self.observe_clean_memo_probe(key)
10556            }
10557        }
10558    }
10559
10560    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
10561        self.clean_memo_probe_samples += 1;
10562        if !self.clean_memo_probe_seen.insert(key.clone()) {
10563            self.clean_memo_probe_repeats += 1;
10564        }
10565        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
10566            self.clean_memo_mode = CleanMemoMode::Promote;
10567            self.clean_memo_probe_seen.clear();
10568            return true;
10569        }
10570        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
10571            self.clean_memo_mode = CleanMemoMode::Sparse;
10572            self.clean_memo_sparse_samples = 0;
10573            self.clean_memo_probe_seen.clear();
10574            return false;
10575        }
10576        true
10577    }
10578
10579    /// Borrows the visible token at an absolute token-stream index.
10580    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
10581        self.input.get(index)
10582    }
10583
10584    /// Returns the compact token ID at an absolute token-stream index.
10585    fn token_id_at(&self, index: usize) -> Option<TokenId> {
10586        self.input.get_id(index)
10587    }
10588
10589    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
10590        let token = self
10591            .token_id_at(index)
10592            .expect("recognized token index must exist in the token store");
10593        let node = if error {
10594            ArenaRecognizedNode::ErrorToken { token }
10595        } else {
10596            ArenaRecognizedNode::Token { token }
10597        };
10598        self.recognition_arena.push_node(node)
10599    }
10600
10601    fn arena_missing_token_node(
10602        &mut self,
10603        token_type: i32,
10604        at_index: usize,
10605        text: String,
10606    ) -> RecognizedNodeId {
10607        let extra = self
10608            .recognition_arena
10609            .push_extra(RecognitionExtra::MissingToken {
10610                token_type,
10611                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
10612                text,
10613            });
10614        self.recognition_arena
10615            .push_node(ArenaRecognizedNode::MissingToken { extra })
10616    }
10617
10618    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
10619        let ArenaRuleSpec {
10620            rule_index,
10621            invoking_state,
10622            alt_number,
10623            start_index,
10624            stop_index,
10625            return_values,
10626            children,
10627        } = spec;
10628        let return_values = (!return_values.is_empty()).then(|| {
10629            self.recognition_arena
10630                .push_extra(RecognitionExtra::ReturnValues(return_values))
10631        });
10632        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
10633            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10634            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
10635            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10636            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
10637            stop_index: stop_index
10638                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
10639            return_values,
10640            children,
10641        })
10642    }
10643
10644    fn arena_boundary_node(&mut self, rule_index: usize, alt_number: usize) -> RecognizedNodeId {
10645        self.recognition_arena
10646            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
10647                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10648                alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10649            })
10650    }
10651
10652    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
10653        *sequence = self.recognition_arena.prepend(*sequence, node);
10654    }
10655
10656    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
10657        self.last_recognition_arena_root = root;
10658        self.last_recognition_arena_diagnostics = diagnostics;
10659        #[cfg(feature = "perf-counters")]
10660        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
10661            let stats = self.recognition_arena_stats();
10662            #[allow(clippy::print_stderr)]
10663            {
10664                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
10665                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
10666                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
10667                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
10668                eprintln!("perf recognition_links_total={}", stats.total_links);
10669                eprintln!("perf recognition_links_live={}", stats.live_links);
10670                eprintln!("perf recognition_links_dead={}", stats.dead_links);
10671                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
10672                eprintln!("perf recognition_extras_total={}", stats.total_extras);
10673                eprintln!("perf recognition_extras_live={}", stats.live_extras);
10674                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
10675                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
10676            }
10677        }
10678    }
10679
10680    fn reset_recognition_arena(&mut self) {
10681        self.recognition_arena.reset();
10682        self.last_recognition_arena_root = NodeSeqId::EMPTY;
10683        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
10684    }
10685
10686    /// Normalizes the current token-stream cursor to the next parser-visible
10687    /// token before capturing a rule start boundary.
10688    fn current_visible_index(&mut self) -> usize {
10689        let index = self.input.index();
10690        self.input.seek(index);
10691        self.input.index()
10692    }
10693
10694    /// Reports whether a child rule reached EOF cleanly while also recording
10695    /// an EOF expectation from a longer path inside that child.
10696    fn child_expected_reaches_clean_eof(
10697        &mut self,
10698        children: &[RecognizeOutcome],
10699        expected: &ExpectedTokens,
10700    ) -> bool {
10701        let Some(index) = expected.index else {
10702            return false;
10703        };
10704        self.token_type_at(index) == TOKEN_EOF
10705            && children
10706                .iter()
10707                .any(|child| child.diagnostics.is_empty() && child.index == index)
10708    }
10709
10710    /// Finds the previous token visible to the parser before `index`.
10711    ///
10712    /// The token stream cursor skips hidden-channel tokens, so subtracting one
10713    /// from a visible-token index can point at whitespace. Parser intervals use
10714    /// this helper to stop at the previous visible token while preserving hidden
10715    /// text inside the rendered interval.
10716    fn previous_token_index(&self, index: usize) -> Option<usize> {
10717        self.input.previous_visible_token_index(index)
10718    }
10719
10720    /// Returns the token-stream index used as a rule stop boundary.
10721    ///
10722    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
10723    /// stop at `index` rather than at the previous visible token.
10724    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
10725        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
10726            Some(index)
10727        } else {
10728            self.previous_token_index(index)
10729        }
10730    }
10731
10732    /// Stop-token index for a rule's `@after` action, matching the boundary that
10733    /// `finish_rule` records on the rule context.
10734    ///
10735    /// A rule that matched EOF leaves the cursor parked on the EOF token
10736    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
10737    /// the current index rather than the previous visible token. Without this,
10738    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
10739    /// report the token before EOF (or `None` for empty input), diverging from
10740    /// the rule context that `finish_rule` builds.
10741    ///
10742    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
10743    /// rule ends right before EOF without matching it (the cursor is parked on
10744    /// EOF, but the rule did not consume it). Prefer
10745    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
10746    /// rule context already recorded with the real flag. Kept for callers without
10747    /// the rule tree in hand.
10748    #[must_use]
10749    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
10750        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
10751        self.rule_stop_token_index(current_index, consumed_eof)
10752    }
10753
10754    /// Stop-token index for a rule's `@after` action, taken from the stop token
10755    /// the rule context already recorded.
10756    ///
10757    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
10758    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
10759    /// the rule context — even when the rule ends immediately before EOF without
10760    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
10761    /// to the cursor-based inference only when the tree carries no rule stop.
10762    #[must_use]
10763    pub fn after_action_stop_index_for_tree(
10764        &mut self,
10765        tree: ParseTree,
10766        current_index: usize,
10767    ) -> Option<usize> {
10768        if let Some(stop) = self
10769            .node(tree)
10770            .as_rule()
10771            .and_then(crate::tree::RuleNodeView::stop_id)
10772        {
10773            return Some(stop.index());
10774        }
10775        self.after_action_stop_index(current_index)
10776    }
10777
10778    /// Start-token index for a rule's `@after` action, taken from the start token
10779    /// the rule context already recorded.
10780    ///
10781    /// `enter_rule` sets the rule context start to the first visible token (it
10782    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
10783    /// `$text` in an `@after` action aligned with the rule context — even when the
10784    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
10785    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
10786    /// the tree carries no rule start.
10787    #[must_use]
10788    pub fn after_action_start_index_for_tree(
10789        &self,
10790        tree: ParseTree,
10791        fallback_index: usize,
10792    ) -> usize {
10793        if let Some(start) = self
10794            .node(tree)
10795            .as_rule()
10796            .and_then(crate::tree::RuleNodeView::start_id)
10797        {
10798            return start.index();
10799        }
10800        fallback_index
10801    }
10802
10803    /// Returns the rule stop token for a selected parse path.
10804    ///
10805    /// EOF transitions do not advance the token-stream cursor, so an EOF match
10806    /// must use the current token rather than the previous visible token.
10807    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
10808        self.rule_stop_token_index(index, consumed_eof)
10809            .and_then(|token_index| self.token_id_at(token_index))
10810    }
10811
10812    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
10813    ///
10814    /// Generated Java reports the failed-predicate message at the current
10815    /// lookahead, then consumes until rule recovery can resume. The metadata
10816    /// runtime models the same visible tree shape by keeping skipped tokens as
10817    /// error nodes and returning from the active rule at EOF.
10818    fn predicate_failure_recovery(
10819        &mut self,
10820        request: PredicateFailureRecovery<'_>,
10821    ) -> RecognizeOutcome {
10822        let PredicateFailureRecovery {
10823            rule_index,
10824            index,
10825            message,
10826            member_values,
10827            return_values,
10828            rule_alt_number,
10829        } = request;
10830        let rule_name = self
10831            .rule_names()
10832            .get(rule_index)
10833            .map_or_else(|| rule_index.to_string(), Clone::clone);
10834        let diagnostic = diagnostic_for_token(
10835            self.token_at(index).as_ref(),
10836            format!("rule {rule_name} {message}"),
10837        );
10838        let mut reversed_nodes = NodeSeqId::EMPTY;
10839        let mut next_index = index;
10840        loop {
10841            let symbol = self.token_type_at(next_index);
10842            if symbol == TOKEN_EOF {
10843                break;
10844            }
10845            let error = self.arena_token_node(next_index, true);
10846            self.arena_prepend(&mut reversed_nodes, error);
10847            let after = self.consume_index(next_index, symbol);
10848            if after == next_index {
10849                break;
10850            }
10851            next_index = after;
10852        }
10853        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
10854        let diagnostics = self
10855            .recognition_arena
10856            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10857        RecognizeOutcome {
10858            index: next_index,
10859            consumed_eof: false,
10860            alt_number: rule_alt_number,
10861            member_values,
10862            return_values,
10863            diagnostics,
10864            decisions: Vec::new(),
10865            actions: Vec::new(),
10866            nodes,
10867        }
10868    }
10869
10870    /// Evaluates a user hook for a predicate coordinate that has no generated
10871    /// runtime table entry.
10872    fn parser_semantic_hook_result(
10873        &mut self,
10874        request: ParserSemanticHookRequest<'_>,
10875    ) -> Option<bool> {
10876        let ParserSemanticHookRequest {
10877            index,
10878            rule_index,
10879            pred_index,
10880            context,
10881            local_int_arg,
10882            member_values,
10883        } = request;
10884        let rule_name = self.rule_names().get(rule_index).cloned();
10885        self.input.seek(index);
10886        let input = &mut self.input;
10887        let semantic_hooks = &mut self.semantic_hooks;
10888        let mut ctx = ParserSemCtx {
10889            input,
10890            tree_storage: &self.tree,
10891            rule_index,
10892            coordinate_index: pred_index,
10893            rule_name,
10894            context,
10895            tree: None,
10896            local_int_arg,
10897            member_values,
10898            action: None,
10899        };
10900        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
10901    }
10902
10903    /// Re-inserts unknown-predicate coordinates recorded before a nested
10904    /// interpreted recognition, preserving order and skipping any the nested
10905    /// call already recorded, so a generated parent's fail-loud coordinates
10906    /// survive descending into an interpreted child.
10907    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
10908        if prior.is_empty() {
10909            return;
10910        }
10911        let mut merged = prior;
10912        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
10913            if !merged.contains(&coordinate) {
10914                merged.push(coordinate);
10915            }
10916        }
10917        self.unknown_predicate_hits = merged;
10918    }
10919
10920    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
10921    /// that has no entry in the generated predicate table.
10922    ///
10923    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
10924    /// the guarded path is abandoned; the parse entry surfaces the recorded
10925    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
10926    /// because a parse that consulted an unknown predicate is unreliable no
10927    /// matter which paths were ultimately selected.
10928    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
10929        apply_unknown_predicate_policy(
10930            self.unknown_predicate_policy,
10931            rule_index,
10932            pred_index,
10933            &mut self.unknown_predicate_hits,
10934        )
10935    }
10936
10937    /// Builds the fail-loud error for unknown predicate coordinates recorded
10938    /// by the current parse, if any.
10939    fn unknown_semantic_error(&self) -> Option<AntlrError> {
10940        use std::fmt::Write as _;
10941        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
10942            return None;
10943        }
10944        let mut message = String::new();
10945        for (rule_index, pred_index) in &self.unknown_predicate_hits {
10946            if !message.is_empty() {
10947                message.push_str("; ");
10948            }
10949            let _ = match self.rule_names().get(*rule_index) {
10950                Some(rule_name) => write!(
10951                    message,
10952                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
10953                ),
10954                None => write!(
10955                    message,
10956                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
10957                ),
10958            };
10959        }
10960        for (rule_index, source_state) in &self.unhandled_action_hits {
10961            if !message.is_empty() {
10962                message.push_str("; ");
10963            }
10964            let _ = match self.rule_names().get(*rule_index) {
10965                Some(rule_name) => write!(
10966                    message,
10967                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
10968                ),
10969                None => write!(
10970                    message,
10971                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
10972                ),
10973            };
10974        }
10975        Some(AntlrError::Unsupported(message))
10976    }
10977
10978    /// Evaluates one lowered predicate expression at the requested input
10979    /// position.
10980    ///
10981    /// This sits in the prediction hot loop, so the context borrows the
10982    /// speculative member state read-only and the rule name by reference —
10983    /// no per-evaluation allocation. Only the hook escape path materializes
10984    /// owned copies, and only when a hook is actually consulted.
10985    fn parser_semir_predicate_matches(
10986        &mut self,
10987        semantics: &ParserSemantics,
10988        predicate: &ParserSemanticPredicate,
10989        request: ParserSemanticHookRequest<'_>,
10990    ) -> bool {
10991        self.input.seek(request.index);
10992        let rule_name = self
10993            .data
10994            .rule_names()
10995            .get(request.rule_index)
10996            .map(String::as_str);
10997        let unknown_predicate_policy = self.unknown_predicate_policy;
10998        let mut ctx = ParserSemIrCtx {
10999            input: &mut self.input,
11000            tree_storage: &self.tree,
11001            semantic_hooks: &mut self.semantic_hooks,
11002            rule_index: request.rule_index,
11003            coordinate_index: request.pred_index,
11004            rule_name,
11005            context: request.context,
11006            local_int_arg: request.local_int_arg,
11007            member_values: request.member_values,
11008            invoked_predicates: &mut self.invoked_predicates,
11009            unknown_predicate_policy,
11010            unknown_predicate_hits: &mut self.unknown_predicate_hits,
11011        };
11012        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
11013    }
11014
11015    fn fast_parser_predicate_matches(
11016        &mut self,
11017        context: Option<FastPredicateContext<'_>>,
11018        transition: ParserTransition<'_>,
11019        index: usize,
11020    ) -> bool {
11021        let Some(context) = context else {
11022            return true;
11023        };
11024        let rule_index = transition.arg0() as usize;
11025        let pred_index = transition.arg1() as usize;
11026        let key = (index, rule_index, pred_index);
11027        if let Some(result) = self.fast_predicate_cache.get(&key) {
11028            return *result;
11029        }
11030        let result = self.parser_predicate_matches(PredicateEval {
11031            index,
11032            rule_index,
11033            pred_index,
11034            predicates: context.predicates,
11035            semantics: context.semantics,
11036            context: None,
11037            local_int_arg: None,
11038            member_values: context.member_values,
11039        });
11040        self.fast_predicate_cache.insert(key, result);
11041        result
11042    }
11043
11044    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
11045        let PredicateEval {
11046            index,
11047            rule_index,
11048            pred_index,
11049            predicates,
11050            semantics,
11051            context,
11052            local_int_arg,
11053            member_values,
11054        } = eval;
11055        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
11056            semantics
11057                .predicates
11058                .iter()
11059                .find(|predicate| {
11060                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
11061                })
11062                .map(|predicate| (semantics, predicate))
11063        }) {
11064            return self.parser_semir_predicate_matches(
11065                semantics,
11066                predicate,
11067                ParserSemanticHookRequest {
11068                    index,
11069                    rule_index,
11070                    pred_index,
11071                    context,
11072                    local_int_arg,
11073                    member_values,
11074                },
11075            );
11076        }
11077        let Some((_, _, predicate)) = predicates
11078            .iter()
11079            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
11080        else {
11081            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
11082                index,
11083                rule_index,
11084                pred_index,
11085                context,
11086                local_int_arg,
11087                member_values,
11088            }) {
11089                return result;
11090            }
11091            return self.unknown_predicate_result(rule_index, pred_index);
11092        };
11093        self.input.seek(index);
11094        match predicate {
11095            ParserPredicate::True => true,
11096            ParserPredicate::False => false,
11097            ParserPredicate::FalseWithMessage { .. } => false,
11098            ParserPredicate::Invoke { value } => {
11099                let key = (rule_index, pred_index);
11100                if !self.invoked_predicates.contains(&key) {
11101                    self.invoked_predicates.push(key);
11102                    use std::io::Write as _;
11103                    let mut stdout = std::io::stdout().lock();
11104                    let _ = writeln!(stdout, "eval={value}");
11105                }
11106                *value
11107            }
11108            ParserPredicate::LookaheadTextEquals { offset, text } => self
11109                .input
11110                .lt(*offset)
11111                .is_some_and(|token| Token::text(&token) == Some(*text)),
11112            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
11113                self.la(*offset) != *token_type
11114            }
11115            ParserPredicate::TokenPairAdjacent => {
11116                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
11117                    return false;
11118                };
11119                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
11120                    return false;
11121                };
11122                first + 1 == second
11123            }
11124            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
11125                .and_then(|context| {
11126                    context
11127                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
11128                        .next()
11129                        .map(crate::tree::RuleNodeView::text)
11130                })
11131                .is_none_or(|actual| actual != *text),
11132            ParserPredicate::LocalIntEquals { value } => {
11133                local_int_arg.is_none_or(|(_, actual)| actual == *value)
11134            }
11135            ParserPredicate::LocalIntLessOrEqual { value } => {
11136                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
11137            }
11138            ParserPredicate::MemberModuloEquals {
11139                member,
11140                modulus,
11141                value,
11142                equals,
11143            } => {
11144                if *modulus == 0 {
11145                    return false;
11146                }
11147                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
11148                (actual == *value) == *equals
11149            }
11150            ParserPredicate::MemberEquals {
11151                member,
11152                value,
11153                equals,
11154            } => {
11155                let actual = member_values.get(member).copied().unwrap_or_default();
11156                (actual == *value) == *equals
11157            }
11158        }
11159    }
11160
11161    /// Returns a generated fail-option message for a predicate coordinate.
11162    fn parser_predicate_failure_message(
11163        &self,
11164        rule_index: usize,
11165        pred_index: usize,
11166        predicates: &[(usize, usize, ParserPredicate)],
11167    ) -> Option<&'static str> {
11168        predicates
11169            .iter()
11170            .find_map(|(rule, pred, predicate)| match predicate {
11171                ParserPredicate::FalseWithMessage { message }
11172                    if *rule == rule_index && *pred == pred_index =>
11173                {
11174                    Some(*message)
11175                }
11176                _ => None,
11177            })
11178    }
11179
11180    /// Returns a generated fail-option message for a `SemIR` predicate
11181    /// coordinate.
11182    pub fn parser_semantic_ir_predicate_failure_message(
11183        &self,
11184        rule_index: usize,
11185        pred_index: usize,
11186        semantics: &ParserSemantics,
11187    ) -> Option<&'static str> {
11188        semantics
11189            .predicates
11190            .iter()
11191            .find(|predicate| {
11192                predicate.rule_index == rule_index && predicate.pred_index == pred_index
11193            })
11194            .and_then(|predicate| predicate.failure_message)
11195    }
11196
11197    /// Returns the token-stream index after consuming `symbol` at `index`.
11198    ///
11199    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
11200    /// index stable and rely on `consumed_eof` to record that EOF was matched.
11201    /// The parser's stream cursor is left untouched: speculative recognition
11202    /// reads ahead by absolute index, so paying for `seek` on every visited
11203    /// state would dominate the hot path. Real consumption is committed by
11204    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
11205    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
11206        if symbol == TOKEN_EOF {
11207            return index;
11208        }
11209        self.input.next_visible_after(index)
11210    }
11211
11212    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
11213    /// decision that failed after consuming a shared prefix.
11214    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
11215        let text = display_input_text(&self.input.text(start_index, error_index));
11216        diagnostic_for_token(
11217            self.token_at(error_index).as_ref(),
11218            format!("no viable alternative at input '{text}'"),
11219        )
11220    }
11221
11222    /// Selects the diagnostic for a failed consuming transition after all
11223    /// recovery repairs have been ruled out.
11224    fn recovery_failure_diagnostic(
11225        &self,
11226        index: usize,
11227        decision_start_index: Option<usize>,
11228        expected_symbols: &BTreeSet<i32>,
11229    ) -> ParserDiagnostic {
11230        if expected_symbols.len() > 1 {
11231            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11232                return self.no_viable_alternative(decision_start, index);
11233            }
11234        }
11235        diagnostic_for_token(
11236            self.token_at(index).as_ref(),
11237            format!(
11238                "mismatched input {} expecting {}",
11239                self.token_at(index)
11240                    .as_ref()
11241                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11242                self.expected_symbols_display(expected_symbols)
11243            ),
11244        )
11245    }
11246
11247    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
11248    /// instead of inserting missing tokens in the caller.
11249    fn eof_rule_recovery_diagnostic(
11250        &self,
11251        index: usize,
11252        expected_symbols: &BTreeSet<i32>,
11253        expected: &ExpectedTokens,
11254    ) -> ParserDiagnostic {
11255        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
11256            &expected.symbols
11257        } else {
11258            expected_symbols
11259        };
11260        diagnostic_for_token(
11261            self.token_at(index).as_ref(),
11262            format!(
11263                "mismatched input {} expecting {}",
11264                self.token_at(index)
11265                    .as_ref()
11266                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11267                self.expected_symbols_display(symbols)
11268            ),
11269        )
11270    }
11271
11272    /// Returns token text for a buffered token interval used by generated
11273    /// `$text` actions.
11274    ///
11275    /// ANTLR treats EOF as a range boundary rather than printable input text,
11276    /// even when an action interval explicitly stops at the EOF token.
11277    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
11278        let Some(stop) = stop else {
11279            return String::new();
11280        };
11281        let stop = if self
11282            .token_at(stop)
11283            .is_some_and(|token| token.token_type() == TOKEN_EOF)
11284        {
11285            let Some(previous) = self.previous_token_index(stop) else {
11286                return String::new();
11287            };
11288            previous
11289        } else {
11290            stop
11291        };
11292        self.input.text(start, stop)
11293    }
11294
11295    /// Resets per-parse prediction diagnostics while keeping the parser-level
11296    /// reporting flag configured by generated harness code.
11297    fn clear_prediction_diagnostics(&mut self) {
11298        self.prediction_diagnostics.clear();
11299        self.reported_prediction_diagnostics.clear();
11300    }
11301
11302    /// Drops every per-parse cache that depends on ATN identity or pins
11303    /// recovery-symbol allocations.
11304    ///
11305    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
11306    /// same parser instance can legally be driven against different grammars
11307    /// in sequence. The four caches reset here are keyed by raw ATN
11308    /// coordinates (state numbers, rule indexes) and would silently hand back
11309    /// entries from a previous ATN if reused — pruning lookahead against the
11310    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
11311    /// for the rest of the process. Clearing them on every parse entry keeps
11312    /// the perf wins (caches still amortize within one parse) without making
11313    /// long-lived parsers leak memory or surface stale ATN data:
11314    ///
11315    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
11316    ///   functions of the ATN's state graph.
11317    /// * `state_expected_cache`, `state_expected_token_cache`,
11318    ///   `rule_stop_reach_cache`, and
11319    ///   `recovery_symbols_intern` together form
11320    ///   the identity invariant that lets `FastRecognizeKey` hash
11321    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
11322    ///   so a stale interned `Rc` cannot outlive its map entry.
11323    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
11324    ///   it is retained here and invalidated lazily when the ATN changes.
11325    fn reset_per_parse_caches(&mut self) {
11326        self.rule_first_set_cache.clear();
11327        self.decision_lookahead_cache.clear();
11328        self.ll1_decision_cache.clear();
11329        self.fast_predicate_cache.clear();
11330        self.rule_stop_reach_cache.clear();
11331        self.clean_memo_mode = CleanMemoMode::Probe;
11332        self.clean_memo_probe_seen.clear();
11333        self.clean_memo_probe_samples = 0;
11334        self.clean_memo_probe_repeats = 0;
11335        self.clean_memo_sparse_samples = 0;
11336        self.recovery_symbols_intern.clear();
11337        self.state_expected_cache.clear();
11338        self.state_expected_token_cache.clear();
11339    }
11340
11341    /// Buffers ANTLR-style diagnostic-listener messages for decision states
11342    /// where multiple clean alternatives survive full-context recognition.
11343    fn record_prediction_diagnostics(
11344        &mut self,
11345        atn: &Atn,
11346        state: AtnState<'_>,
11347        start_index: usize,
11348        outcomes: &[RecognizeOutcome],
11349    ) {
11350        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
11351            return;
11352        }
11353        let Some(decision) = atn
11354            .decision_to_state()
11355            .iter()
11356            .position(|state_number| state_number == state.state_number())
11357        else {
11358            return;
11359        };
11360        let Some(rule_index) = state.rule_index() else {
11361            return;
11362        };
11363        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
11364        for outcome in outcomes
11365            .iter()
11366            .filter(|outcome| outcome.diagnostics.is_empty())
11367        {
11368            let Some(alt) = outcome.decisions.first() else {
11369                continue;
11370            };
11371            alts_by_end
11372                .entry(outcome.index)
11373                .or_default()
11374                .insert(alt + 1);
11375        }
11376        let Some((&end_index, ambig_alts)) = alts_by_end
11377            .iter()
11378            .filter(|(_, alts)| alts.len() > 1)
11379            .max_by_key(|(end, _)| *end)
11380        else {
11381            return;
11382        };
11383        let rule_name = self
11384            .rule_names()
11385            .get(rule_index)
11386            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
11387        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
11388        let input = display_input_text(&self.input.text(start_index, stop_index));
11389        let alts = ambig_alts
11390            .iter()
11391            .map(usize::to_string)
11392            .collect::<Vec<_>>()
11393            .join(", ");
11394        let key = (decision, start_index, format!("{alts}:{input}"));
11395        if !self.reported_prediction_diagnostics.insert(key) {
11396            return;
11397        }
11398        let start_diagnostic = diagnostic_for_token(
11399            self.token_at(start_index),
11400            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
11401        );
11402        let stop_diagnostic = diagnostic_for_token(
11403            self.token_at(stop_index),
11404            format!(
11405                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
11406            ),
11407        );
11408        self.prediction_diagnostics.push(start_diagnostic);
11409        self.prediction_diagnostics.push(stop_diagnostic);
11410    }
11411
11412    /// Formats the tokens expected from an ATN state using ANTLR display names.
11413    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11414        expected_symbols_display(
11415            &state_expected_symbols(atn, state_number),
11416            self.vocabulary(),
11417        )
11418    }
11419
11420    /// Expected-token set at the parser's current ATN state — ANTLR's
11421    /// `getExpectedTokens()`. Generated recognizers expose this as
11422    /// `self.expected_tokens()` for embedded test actions
11423    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
11424    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
11425        let state = usize::try_from(self.data().state()).unwrap_or(0);
11426        ExpectedTokenSet {
11427            symbols: state_expected_symbols(atn, state),
11428        }
11429    }
11430
11431    /// Enables the bail error strategy: the first syntax error aborts the
11432    /// parse instead of recovering.
11433    pub const fn set_bail_on_error(&mut self, bail: bool) {
11434        self.bail_on_error = bail;
11435    }
11436
11437    /// Whether the bail error strategy is active.
11438    #[must_use]
11439    pub const fn bail_on_error(&self) -> bool {
11440        self.bail_on_error
11441    }
11442
11443    /// Names of the rules on the live invocation stack, current rule first —
11444    /// ANTLR's `getRuleInvocationStack()`.
11445    pub fn rule_invocation_stack(&self) -> Vec<String> {
11446        self.rule_context_stack
11447            .iter()
11448            .rev()
11449            .map(|frame| {
11450                self.data()
11451                    .rule_names()
11452                    .get(frame.rule_index)
11453                    .cloned()
11454                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
11455            })
11456            .collect()
11457    }
11458
11459    /// Invoking-state chain for the active rule context, current rule first.
11460    ///
11461    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
11462    pub fn active_invocation_states(&self) -> Vec<isize> {
11463        self.rule_context_stack
11464            .iter()
11465            .skip(1)
11466            .rev()
11467            .map(|frame| frame.invoking_state)
11468            .collect()
11469    }
11470
11471    /// Formats a buffered token in ANTLR's diagnostic token display form.
11472    pub fn token_display_at(&self, index: usize) -> Option<String> {
11473        self.token_at(index).map(|token| format!("{token}"))
11474    }
11475}
11476
11477impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
11478where
11479    S: TokenSource,
11480    H: SemanticHooks,
11481{
11482    fn parse_rule(
11483        &mut self,
11484        rule_index: usize,
11485        invoking_state: isize,
11486        precedence: i32,
11487    ) -> DirectAdaptiveParseResult<ParseTree> {
11488        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
11489            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
11490        )?;
11491        let stop_state = self
11492            .atn
11493            .rule_to_stop_state()
11494            .get(rule_index)
11495            .filter(|state| *state != usize::MAX)
11496            .ok_or(DirectAdaptiveParseControl::Fallback(
11497                DirectAdaptiveFallback::MissingAtn,
11498            ))?;
11499        let start_index = self.parser.current_visible_index();
11500        let mut context = ParserRuleContext::new(rule_index, invoking_state);
11501        if let Some(token) = self.parser.token_id_at(start_index) {
11502            self.parser.set_context_start(&mut context, token);
11503        }
11504        let mut state_number = start_state;
11505        let mut consumed_eof = false;
11506        while state_number != stop_state {
11507            self.step()?;
11508            let (transition, boundary) = self.next_transition(state_number, precedence)?;
11509            if boundary.is_some() {
11510                return Err(DirectAdaptiveParseControl::Fallback(
11511                    DirectAdaptiveFallback::LeftRecursiveBoundary,
11512                ));
11513            }
11514            match transition.data() {
11515                Transition::Epsilon { target } => {
11516                    state_number = target;
11517                }
11518                Transition::Precedence {
11519                    target,
11520                    precedence: transition_precedence,
11521                } => {
11522                    if transition_precedence < precedence {
11523                        return Err(DirectAdaptiveParseControl::Fallback(
11524                            DirectAdaptiveFallback::Precedence,
11525                        ));
11526                    }
11527                    state_number = target;
11528                }
11529                Transition::Rule {
11530                    rule_index,
11531                    follow_state,
11532                    precedence: rule_precedence,
11533                    ..
11534                } => {
11535                    let child = self.parse_rule(
11536                        rule_index,
11537                        invoking_state_number(state_number),
11538                        rule_precedence,
11539                    )?;
11540                    if self.parser.build_parse_trees {
11541                        self.parser.tree.add_child(&mut context, child);
11542                    }
11543                    state_number = follow_state;
11544                }
11545                Transition::Atom { .. }
11546                | Transition::Range { .. }
11547                | Transition::Set { .. }
11548                | Transition::NotSet { .. }
11549                | Transition::Wildcard { .. } => {
11550                    let (matched_eof, child) = self.consume_transition(transition)?;
11551                    consumed_eof |= matched_eof;
11552                    if let Some(child) = child {
11553                        self.parser.tree.add_child(&mut context, child);
11554                    }
11555                    state_number = transition.target();
11556                }
11557                Transition::Predicate { .. } => {
11558                    return Err(DirectAdaptiveParseControl::Fallback(
11559                        DirectAdaptiveFallback::Predicate,
11560                    ));
11561                }
11562                Transition::Action { .. } => {
11563                    return Err(DirectAdaptiveParseControl::Fallback(
11564                        DirectAdaptiveFallback::Action,
11565                    ));
11566                }
11567            }
11568        }
11569
11570        let stop_index = self
11571            .parser
11572            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
11573        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
11574            self.parser.set_context_stop(&mut context, token);
11575        }
11576        Ok(self.parser.rule_node(context))
11577    }
11578
11579    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
11580        self.steps += 1;
11581        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
11582            return Err(DirectAdaptiveParseControl::Fallback(
11583                DirectAdaptiveFallback::StepLimit,
11584            ));
11585        }
11586        Ok(())
11587    }
11588
11589    fn next_transition(
11590        &mut self,
11591        state_number: usize,
11592        precedence: i32,
11593    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
11594        let state = self
11595            .atn
11596            .state(state_number)
11597            .ok_or(DirectAdaptiveParseControl::Fallback(
11598                DirectAdaptiveFallback::MissingAtn,
11599            ))?;
11600        if state.is_rule_stop() {
11601            return Err(DirectAdaptiveParseControl::Fallback(
11602                DirectAdaptiveFallback::RuleStop,
11603            ));
11604        }
11605        let transition_index =
11606            self.transition_index(state_number, state.transitions().len(), precedence)?;
11607        let transition = state.transitions().get(transition_index).ok_or(
11608            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
11609        )?;
11610        let boundary = match &transition.data() {
11611            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
11612                left_recursive_boundary(self.atn, state, *target)
11613            }
11614            _ => None,
11615        };
11616        Ok((transition, boundary))
11617    }
11618
11619    fn transition_index(
11620        &mut self,
11621        state_number: usize,
11622        transition_count: usize,
11623        precedence: i32,
11624    ) -> DirectAdaptiveParseResult<usize> {
11625        match transition_count {
11626            0 => Err(DirectAdaptiveParseControl::Fallback(
11627                DirectAdaptiveFallback::NoTransition,
11628            )),
11629            1 => Ok(0),
11630            _ => {
11631                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
11632                    return Ok(alt);
11633                }
11634                let decision = self
11635                    .decision_by_state
11636                    .get(state_number)
11637                    .and_then(|decision| *decision)
11638                    .ok_or(DirectAdaptiveParseControl::Fallback(
11639                        DirectAdaptiveFallback::UnknownDecision,
11640                    ))?;
11641                let prediction = self
11642                    .simulator
11643                    .adaptive_predict_stream_info_with_precedence(
11644                        decision,
11645                        direct_precedence(precedence),
11646                        &mut self.parser.input,
11647                    )
11648                    .map_err(|_| {
11649                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
11650                    })?;
11651                if prediction.has_semantic_context {
11652                    return Err(DirectAdaptiveParseControl::Fallback(
11653                        DirectAdaptiveFallback::SemanticContext,
11654                    ));
11655                }
11656                prediction
11657                    .alt
11658                    .checked_sub(1)
11659                    .filter(|index| *index < transition_count)
11660                    .ok_or(DirectAdaptiveParseControl::Fallback(
11661                        DirectAdaptiveFallback::InvalidAlt,
11662                    ))
11663            }
11664        }
11665    }
11666
11667    fn ll1_transition_index(
11668        &mut self,
11669        state_number: usize,
11670        transition_count: usize,
11671    ) -> DirectAdaptiveParseResult<Option<usize>> {
11672        let state = self
11673            .atn
11674            .state(state_number)
11675            .ok_or(DirectAdaptiveParseControl::Fallback(
11676                DirectAdaptiveFallback::MissingAtn,
11677            ))?;
11678        if state.precedence_rule_decision() {
11679            return Ok(None);
11680        }
11681        let Some(rule_stop) = state
11682            .rule_index()
11683            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
11684        else {
11685            return Ok(None);
11686        };
11687        let symbol = self.parser.input.la_token(1);
11688        let entry = self
11689            .parser
11690            .cached_decision_lookahead(self.atn, state, rule_stop);
11691        Ok(
11692            ll1_greedy_alt(&entry, symbol, state.non_greedy())
11693                .filter(|alt| *alt < transition_count),
11694        )
11695    }
11696
11697    fn consume_transition(
11698        &mut self,
11699        transition: ParserTransition<'_>,
11700    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
11701        let symbol = self.parser.input.la_token(1);
11702        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
11703            return Err(DirectAdaptiveParseControl::Fallback(
11704                DirectAdaptiveFallback::TokenMismatch,
11705            ));
11706        }
11707        let token = self
11708            .parser
11709            .input
11710            .lt_id(1)
11711            .ok_or(DirectAdaptiveParseControl::Fallback(
11712                DirectAdaptiveFallback::TokenMismatch,
11713            ))?;
11714        let matched_eof = symbol == TOKEN_EOF;
11715        if !matched_eof {
11716            self.parser.consume();
11717        }
11718        let child = self
11719            .parser
11720            .build_parse_trees
11721            .then(|| self.parser.terminal_tree(token));
11722        Ok((matched_eof, child))
11723    }
11724}
11725
11726/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
11727/// transformed left-recursive rule.
11728fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
11729    if !state.precedence_rule_decision() {
11730        return None;
11731    }
11732    let target_state = atn.state(target)?;
11733    if target_state.kind() == AtnStateKind::LoopEnd {
11734        return None;
11735    }
11736    state.rule_index()
11737}
11738
11739/// Selects the first outer alternative observed for a rule path.
11740///
11741/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
11742/// outer decision. The metadata recognizer only needs this when a generated
11743/// grammar opts into that target template; otherwise the value remains `0` and
11744/// parse-tree rendering is unchanged.
11745fn next_alt_number(
11746    state: AtnState<'_>,
11747    transition_count: usize,
11748    transition_index: usize,
11749    current_alt_number: usize,
11750    track_alt_numbers: bool,
11751) -> usize {
11752    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
11753        return current_alt_number;
11754    }
11755    if matches!(
11756        state.kind(),
11757        AtnStateKind::Basic
11758            | AtnStateKind::BlockStart
11759            | AtnStateKind::PlusBlockStart
11760            | AtnStateKind::StarBlockStart
11761            | AtnStateKind::StarLoopEntry
11762    ) && !state.precedence_rule_decision()
11763    {
11764        return transition_index + 1;
11765    }
11766    current_alt_number
11767}
11768
11769/// Converts an ATN state number into the signed invoking-state slot used by
11770/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
11771fn invoking_state_number(state_number: usize) -> isize {
11772    isize::try_from(state_number).unwrap_or(isize::MAX)
11773}
11774
11775const fn packed_i32(value: u32) -> i32 {
11776    i32::from_le_bytes(value.to_le_bytes())
11777}
11778
11779fn direct_precedence(precedence: i32) -> usize {
11780    usize::try_from(precedence.max(0)).unwrap_or_default()
11781}
11782
11783fn token_input_display(token: &impl Token) -> String {
11784    format!("'{}'", token.text().unwrap_or("<EOF>"))
11785}
11786
11787fn display_input_text(text: &str) -> String {
11788    let mut out = String::new();
11789    for ch in text.chars() {
11790        match ch {
11791            '\n' => out.push_str("\\n"),
11792            '\r' => out.push_str("\\r"),
11793            '\t' => out.push_str("\\t"),
11794            other => out.push(other),
11795        }
11796    }
11797    out
11798}
11799
11800fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
11801    let (line, column) = token.map_or((0, 0), |token| (token.line(), token.column()));
11802    ParserDiagnostic {
11803        line,
11804        column,
11805        message,
11806    }
11807}
11808
11809fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
11810    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
11811}
11812
11813fn expected_symbols_display_iter(
11814    symbols: impl IntoIterator<Item = i32>,
11815    vocabulary: &Vocabulary,
11816) -> String {
11817    let items = symbols
11818        .into_iter()
11819        .map(|symbol| expected_symbol_display(symbol, vocabulary))
11820        .collect::<Vec<_>>();
11821    if let [single] = items.as_slice() {
11822        return single.clone();
11823    }
11824    format!("{{{}}}", items.join(", "))
11825}
11826
11827fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
11828    if symbol == TOKEN_EOF {
11829        return "<EOF>".to_owned();
11830    }
11831    vocabulary.display_name(symbol)
11832}
11833
11834fn caller_follow_token_info_for_stream<S: TokenSource>(
11835    input: &mut CommonTokenStream<S>,
11836    index: usize,
11837) -> (i32, bool, bool) {
11838    // Generated callers own statement separators; leave them available when
11839    // an interpreted child rule can either stop before or consume one.
11840    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
11841        input.fill();
11842    }
11843    let token_type = input.token_type_at_index(index);
11844    let visible_channel = input.channel();
11845    let token = input.get(index);
11846    let is_boundary = token
11847        .as_ref()
11848        .and_then(Token::text)
11849        .is_some_and(is_caller_follow_boundary_text);
11850    let is_boundary_gap = token.as_ref().is_some_and(|token| {
11851        token.channel() != visible_channel
11852            || is_caller_follow_boundary_gap_text(token.text_or_empty())
11853    });
11854    (token_type, is_boundary, is_boundary_gap)
11855}
11856
11857fn is_caller_follow_boundary_text(text: &str) -> bool {
11858    text.chars().any(|ch| ch == ';' || ch == '\n')
11859        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11860}
11861
11862fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
11863    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11864}
11865
11866/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
11867/// Inline insertion in those precedence loops can synthesize a missing operand
11868/// before an operator and then block the legitimate loop-exit path.
11869fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
11870    let Some(rule_index) = state.rule_index() else {
11871        return false;
11872    };
11873    atn.rule_to_start_state()
11874        .get(rule_index)
11875        .and_then(|state_number| atn.state(state_number))
11876        .is_some_and(AtnState::left_recursive_rule)
11877}
11878
11879/// Picks the better of two `parse_atn_rule` passes (with and without the
11880/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
11881/// recovered one; among recovered outcomes the second pass is preferred
11882/// because the no-prefilter walk reaches ANTLR-style recovery inside child
11883/// rules. If both passes failed, the second pass's expected-token snapshot
11884/// is returned so the caller renders the same diagnostic ANTLR would.
11885fn select_better_top_outcome(
11886    first: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
11887    second: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
11888    arena: &RecognitionArena,
11889) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
11890    match (first, second) {
11891        (Ok(first), Ok(second)) => {
11892            if arena.diagnostics(first.0.diagnostics).next().is_none() {
11893                Ok(first)
11894            } else {
11895                Ok(second)
11896            }
11897        }
11898        (Ok(first), Err(_)) => Ok(first),
11899        (Err(_), Ok(second)) => Ok(second),
11900        (Err(_), Err(second_expected)) => Err(second_expected),
11901    }
11902}
11903
11904/// Chooses the outermost parse result that consumed the most input.
11905///
11906/// The recognizer intentionally keeps shorter endpoints available while walking
11907/// nested rule transitions so callers can satisfy following tokens such as
11908/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
11909fn select_best_fast_outcome(
11910    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
11911    prediction_mode: PredictionMode,
11912    caller_follow: Option<&TokenBitSet>,
11913    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
11914    arena: &RecognitionArena,
11915) -> Option<FastRecognizeOutcome> {
11916    let mut best = None;
11917    let mut best_caller_follow = None;
11918    for outcome in outcomes {
11919        if matches!(
11920            prediction_mode,
11921            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11922        ) && outcome.diagnostics.is_empty()
11923            && let Some(follow) = caller_follow
11924        {
11925            let (token_type, is_boundary, _) = token_info_at(outcome.index);
11926            if is_boundary && follow.contains(token_type) {
11927                let replace =
11928                    best_caller_follow
11929                        .as_ref()
11930                        .is_none_or(|existing: &FastRecognizeOutcome| {
11931                            (outcome.index, outcome.consumed_eof)
11932                                < (existing.index, existing.consumed_eof)
11933                        });
11934                if replace {
11935                    best_caller_follow = Some(outcome);
11936                }
11937            }
11938        }
11939        let Some(existing) = best else {
11940            best = Some(outcome);
11941            continue;
11942        };
11943        let outcome_position = (outcome.index, outcome.consumed_eof);
11944        let best_position = (existing.index, existing.consumed_eof);
11945        let better = match prediction_mode {
11946            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
11947                outcome_position,
11948                outcome.diagnostics,
11949                best_position,
11950                existing.diagnostics,
11951                arena,
11952            ),
11953            PredictionMode::Sll => outcome.index > existing.index,
11954        };
11955        best = Some(if better { outcome } else { existing });
11956    }
11957    let should_use_caller_follow =
11958        best_caller_follow
11959            .as_ref()
11960            .zip(best.as_ref())
11961            .is_some_and(|(candidate, selected)| {
11962                if !selected.diagnostics.is_empty() {
11963                    return true;
11964                }
11965                candidate.index < selected.index
11966                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
11967            });
11968    if should_use_caller_follow {
11969        best_caller_follow
11970    } else {
11971        best
11972    }
11973}
11974
11975fn select_best_outcome(
11976    outcomes: impl Iterator<Item = RecognizeOutcome>,
11977    prediction_mode: PredictionMode,
11978    arena: &RecognitionArena,
11979) -> Option<RecognizeOutcome> {
11980    let outcomes = outcomes.collect::<Vec<_>>();
11981    let prefer_first_tie = outcomes
11982        .iter()
11983        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
11984    outcomes.into_iter().reduce(|best, outcome| {
11985        let outcome_position = (outcome.index, outcome.consumed_eof);
11986        let best_position = (best.index, best.consumed_eof);
11987        let better = match prediction_mode {
11988            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
11989                outcome_is_better(
11990                    outcome_position,
11991                    outcome.diagnostics,
11992                    best_position,
11993                    best.diagnostics,
11994                    arena,
11995                ) || (outcome_position == best_position
11996                    && arena.diagnostics_len(outcome.diagnostics)
11997                        == arena.diagnostics_len(best.diagnostics)
11998                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
11999                        == arena.diagnostics_recovery_rank(best.diagnostics)
12000                    && (outcome.decisions < best.decisions
12001                        || (!prefer_first_tie
12002                            && outcome.decisions == best.decisions
12003                            && outcome.actions > best.actions)))
12004            }
12005            PredictionMode::Sll => {
12006                outcome_position > best_position
12007                    || (outcome_position == best_position
12008                        && !prefer_first_tie
12009                        && (outcome.decisions < best.decisions
12010                            || (outcome.decisions == best.decisions
12011                                && outcome_is_better(
12012                                    outcome_position,
12013                                    outcome.diagnostics,
12014                                    best_position,
12015                                    best.diagnostics,
12016                                    arena,
12017                                ))))
12018            }
12019        };
12020        if better {
12021            return outcome;
12022        }
12023        best
12024    })
12025}
12026
12027/// Records the serialized transition order at parser decision states.
12028///
12029/// When two clean paths consume the same input, ANTLR's adaptive prediction
12030/// chooses by alternative order. Keeping this compact trace lets the metadata
12031/// recognizer distinguish greedy and non-greedy optional blocks without a full
12032/// prediction simulator.
12033fn transition_decision(
12034    atn: &Atn,
12035    state: AtnState<'_>,
12036    transition_count: usize,
12037    transition_index: usize,
12038    predicates: &[(usize, usize, ParserPredicate)],
12039) -> Option<usize> {
12040    if transition_count <= 1 || decision_reaches_unsupported_predicate(atn, state, predicates) {
12041        return None;
12042    }
12043    Some(transition_index)
12044}
12045
12046/// Reports whether a state should reset the active no-viable decision start.
12047///
12048/// Loop entry/back states are continuations of the surrounding adaptive
12049/// prediction; resetting at those states would turn LL-star failures back into
12050/// ordinary mismatches.
12051fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
12052    transition_count > 1
12053        && !matches!(
12054            state.kind(),
12055            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
12056        )
12057}
12058
12059/// Marks a farthest expected-token set as no-viable when multiple alternatives
12060/// failed after the active decision had already consumed input.
12061fn record_no_viable_if_ambiguous(
12062    expected: &mut ExpectedTokens,
12063    decision_start_index: Option<usize>,
12064    index: usize,
12065) {
12066    if expected.index == Some(index) && expected.symbols.len() > 1 {
12067        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
12068            expected.record_no_viable(decision_start, index);
12069        }
12070    }
12071}
12072
12073/// Records a no-viable decision caused by a failed semantic predicate before
12074/// any consuming transition can contribute an expected-token set.
12075const fn record_predicate_no_viable(
12076    expected: &mut ExpectedTokens,
12077    decision_start_index: Option<usize>,
12078    index: usize,
12079) {
12080    if let Some(decision_start) = decision_start_index {
12081        expected.record_no_viable(decision_start, index);
12082    }
12083}
12084
12085/// Returns the active decision start only when the error is past that start.
12086const fn no_viable_decision_start(
12087    decision_start_index: Option<usize>,
12088    index: usize,
12089) -> Option<usize> {
12090    match decision_start_index {
12091        Some(start) if index > start => Some(start),
12092        _ => None,
12093    }
12094}
12095
12096/// Restores expected-token bookkeeping when a child rule found a clean
12097/// consuming path; failures in longer child alternatives should not pollute the
12098/// caller's final expectation set.
12099fn restore_expected(
12100    children: &[RecognizeOutcome],
12101    child_start_index: usize,
12102    expected: &mut ExpectedTokens,
12103    snapshot: ExpectedTokens,
12104    preserve_child_expected: bool,
12105) {
12106    if preserve_child_expected {
12107        return;
12108    }
12109    if children
12110        .iter()
12111        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
12112    {
12113        *expected = snapshot;
12114    }
12115}
12116
12117/// Reports whether a decision can reach a predicate the generator did not
12118/// translate. Static alternative order is unsafe for those context predicates.
12119fn decision_reaches_unsupported_predicate(
12120    atn: &Atn,
12121    state: AtnState<'_>,
12122    predicates: &[(usize, usize, ParserPredicate)],
12123) -> bool {
12124    state.transitions().iter().any(|transition| {
12125        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
12126    })
12127}
12128
12129/// Walks epsilon-like edges from one transition to find unsupported predicates.
12130fn transition_reaches_unsupported_predicate(
12131    atn: &Atn,
12132    transition: ParserTransition<'_>,
12133    predicates: &[(usize, usize, ParserPredicate)],
12134    visited: &mut BTreeSet<usize>,
12135) -> bool {
12136    match &transition.data() {
12137        Transition::Predicate {
12138            rule_index,
12139            pred_index,
12140            ..
12141        } => !predicates
12142            .iter()
12143            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
12144        Transition::Epsilon { target }
12145        | Transition::Action { target, .. }
12146        | Transition::Rule { target, .. } => {
12147            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
12148        }
12149        Transition::Precedence { .. }
12150        | Transition::Atom { .. }
12151        | Transition::Range { .. }
12152        | Transition::Set { .. }
12153        | Transition::NotSet { .. }
12154        | Transition::Wildcard { .. } => false,
12155    }
12156}
12157
12158/// Finds an unsupported predicate reachable before a consuming transition.
12159fn state_reaches_unsupported_predicate(
12160    atn: &Atn,
12161    state_number: usize,
12162    predicates: &[(usize, usize, ParserPredicate)],
12163    visited: &mut BTreeSet<usize>,
12164) -> bool {
12165    if !visited.insert(state_number) {
12166        return false;
12167    }
12168    let Some(state) = atn.state(state_number) else {
12169        return false;
12170    };
12171    state.transitions().iter().any(|transition| {
12172        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
12173    })
12174}
12175
12176/// Adds a decision step to the front of an already-recognized suffix path.
12177fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
12178    if let Some(decision) = decision {
12179        outcome.decisions.insert(0, decision);
12180    }
12181}
12182
12183fn outcome_is_better(
12184    outcome_position: (usize, bool),
12185    outcome_diagnostics: DiagnosticSeqId,
12186    best_position: (usize, bool),
12187    best_diagnostics: DiagnosticSeqId,
12188    arena: &RecognitionArena,
12189) -> bool {
12190    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
12191    let best_len = arena.diagnostics_len(best_diagnostics);
12192    outcome_position > best_position
12193        || (outcome_position == best_position
12194            && (outcome_len < best_len
12195                || (outcome_len == best_len
12196                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
12197                        < arena.diagnostics_recovery_rank(best_diagnostics))))
12198}
12199
12200fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
12201    if outcomes
12202        .iter()
12203        .any(|outcome| outcome.diagnostics.is_empty())
12204    {
12205        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12206    }
12207}
12208
12209fn discard_recovered_outcomes_if_clean_path_exists(
12210    outcomes: &mut Vec<RecognizeOutcome>,
12211    arena: &RecognitionArena,
12212) {
12213    if outcomes
12214        .iter()
12215        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
12216    {
12217        return;
12218    }
12219    if outcomes
12220        .iter()
12221        .any(|outcome| outcome.diagnostics.is_empty())
12222    {
12223        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12224    }
12225}
12226
12227/// Reports whether a recovered outcome came from an explicit predicate
12228/// fail-option and therefore should compete with shorter clean loop exits.
12229fn outcome_has_rule_failure_diagnostic(
12230    outcome: &RecognizeOutcome,
12231    arena: &RecognitionArena,
12232) -> bool {
12233    arena
12234        .diagnostics(outcome.diagnostics)
12235        .any(|diagnostic| diagnostic.message.starts_with("rule "))
12236}
12237
12238/// Removes equivalent endpoints before memoizing a state result while
12239/// preserving ATN transition-discovery order.
12240///
12241/// Outcomes are compared on observable recognition state — the input index,
12242/// EOF consumption, and diagnostics — without descending into the parse-tree
12243/// fragment carried by `nodes`. Two paths reaching the same point with
12244/// different node trees would otherwise prevent memoization from collapsing
12245/// equivalent suffixes and explode the speculative-path cache.
12246///
12247/// The first occurrence per recognition key wins, which matches ANTLR's
12248/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
12249/// transitions before loop-exit, so the first-discovered outcome carries the
12250/// greedy parse-tree fragment.
12251fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
12252    if outcomes.len() < 2 {
12253        return;
12254    }
12255    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
12256    outcomes.retain(|outcome| {
12257        seen.insert((
12258            outcome.index,
12259            outcome.consumed_eof,
12260            arena.diagnostics_len(outcome.diagnostics),
12261            arena.diagnostics_recovery_rank(outcome.diagnostics),
12262        ))
12263    });
12264}
12265
12266const FAST_OUTCOME_INLINE_KEYS: usize = 8;
12267const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
12268const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
12269const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
12270
12271#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12272enum FastOutcomeDedupStrategy {
12273    Inline,
12274    Dense,
12275    Sparse,
12276}
12277
12278impl FastOutcomeDedupScratch {
12279    fn prepare_dense(&mut self, word_count: usize) {
12280        while let Some(word_index) = self.touched_dense_words.pop() {
12281            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
12282        }
12283        if self.dense_words.len() < word_count {
12284            self.dense_words.resize(word_count, 0);
12285        }
12286    }
12287}
12288
12289fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
12290    let first_index = outcomes.first()?.index;
12291    let (min_index, max_index) = outcomes[1..].iter().fold(
12292        (first_index, first_index),
12293        |(min_index, max_index), outcome| {
12294            (min_index.min(outcome.index), max_index.max(outcome.index))
12295        },
12296    );
12297    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
12298    let bit_count = index_span.checked_mul(2)?;
12299    let word_count =
12300        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
12301    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
12302    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
12303    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
12304        .then_some((min_index, word_count))
12305}
12306
12307#[cfg(feature = "perf-counters")]
12308fn record_clean_fast_outcome_dedup(
12309    strategy: FastOutcomeDedupStrategy,
12310    input_len: usize,
12311    output_len: usize,
12312    dense_words: usize,
12313) {
12314    let counter = match strategy {
12315        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
12316        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
12317        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
12318    };
12319    perf_counters::inc(
12320        &perf_counters::OUTCOME_DEDUPE_INPUTS,
12321        u64::try_from(input_len).unwrap_or(u64::MAX),
12322    );
12323    perf_counters::inc(
12324        &perf_counters::OUTCOME_DEDUPE_REMOVED,
12325        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
12326    );
12327    perf_counters::inc(counter, 1);
12328    perf_counters::inc(
12329        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
12330        u64::try_from(dense_words).unwrap_or(u64::MAX),
12331    );
12332}
12333
12334/// Removes duplicate clean endpoints while preserving transition-discovery
12335/// order. Tiny lists stay on the stack; larger compact ranges use a direct
12336/// bitmap, and only wide sparse ranges pay for hashing.
12337fn dedupe_clean_fast_outcomes(
12338    outcomes: &mut Vec<FastRecognizeOutcome>,
12339    scratch: &mut FastOutcomeDedupScratch,
12340) -> FastOutcomeDedupStrategy {
12341    #[cfg(feature = "perf-counters")]
12342    let input_len = outcomes.len();
12343    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
12344        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
12345        let mut inline_len = 0_usize;
12346        outcomes.retain(|outcome| {
12347            let key = (outcome.index, outcome.consumed_eof);
12348            if inline_keys[..inline_len].contains(&key) {
12349                return false;
12350            }
12351            inline_keys[inline_len] = key;
12352            inline_len += 1;
12353            true
12354        });
12355        #[cfg(feature = "perf-counters")]
12356        record_clean_fast_outcome_dedup(
12357            FastOutcomeDedupStrategy::Inline,
12358            input_len,
12359            outcomes.len(),
12360            0,
12361        );
12362        return FastOutcomeDedupStrategy::Inline;
12363    }
12364
12365    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
12366        scratch.prepare_dense(word_count);
12367        outcomes.retain(|outcome| {
12368            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
12369            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
12370            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
12371            let word = &mut scratch.dense_words[word_index];
12372            if *word & bit != 0 {
12373                return false;
12374            }
12375            if *word == 0 {
12376                scratch
12377                    .touched_dense_words
12378                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
12379            }
12380            *word |= bit;
12381            true
12382        });
12383        #[cfg(feature = "perf-counters")]
12384        record_clean_fast_outcome_dedup(
12385            FastOutcomeDedupStrategy::Dense,
12386            input_len,
12387            outcomes.len(),
12388            word_count,
12389        );
12390        return FastOutcomeDedupStrategy::Dense;
12391    }
12392
12393    scratch.sparse_keys.clear();
12394    scratch.sparse_keys.reserve(outcomes.len());
12395    outcomes.retain(|outcome| {
12396        scratch
12397            .sparse_keys
12398            .insert((outcome.index, outcome.consumed_eof))
12399    });
12400    #[cfg(feature = "perf-counters")]
12401    record_clean_fast_outcome_dedup(
12402        FastOutcomeDedupStrategy::Sparse,
12403        input_len,
12404        outcomes.len(),
12405        0,
12406    );
12407    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
12408        scratch.sparse_keys = FxHashSet::default();
12409    }
12410    FastOutcomeDedupStrategy::Sparse
12411}
12412
12413/// Sorts and removes equivalent endpoints, including action traces and the
12414/// arena-backed node sequence's structural contents.
12415fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
12416    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
12417    outcomes
12418        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
12419}
12420
12421fn compare_recognize_outcomes(
12422    left: &RecognizeOutcome,
12423    right: &RecognizeOutcome,
12424    arena: &RecognitionArena,
12425) -> Ordering {
12426    left.index
12427        .cmp(&right.index)
12428        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
12429        .then_with(|| left.alt_number.cmp(&right.alt_number))
12430        .then_with(|| left.member_values.cmp(&right.member_values))
12431        .then_with(|| left.return_values.cmp(&right.return_values))
12432        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
12433        .then_with(|| left.decisions.cmp(&right.decisions))
12434        .then_with(|| left.actions.cmp(&right.actions))
12435        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
12436}
12437
12438impl<S, H> Recognizer for BaseParser<S, H>
12439where
12440    S: TokenSource,
12441    H: SemanticHooks,
12442{
12443    fn data(&self) -> &RecognizerData {
12444        &self.data
12445    }
12446
12447    fn data_mut(&mut self) -> &mut RecognizerData {
12448        &mut self.data
12449    }
12450}
12451
12452impl<S, H> Parser for BaseParser<S, H>
12453where
12454    S: TokenSource,
12455    H: SemanticHooks,
12456{
12457    fn build_parse_trees(&self) -> bool {
12458        self.build_parse_trees
12459    }
12460
12461    fn set_build_parse_trees(&mut self, build: bool) {
12462        self.build_parse_trees = build;
12463    }
12464
12465    fn number_of_syntax_errors(&self) -> usize {
12466        Self::number_of_syntax_errors(self)
12467    }
12468
12469    fn report_diagnostic_errors(&self) -> bool {
12470        self.report_diagnostic_errors
12471    }
12472
12473    fn set_report_diagnostic_errors(&mut self, report: bool) {
12474        self.report_diagnostic_errors = report;
12475    }
12476
12477    fn prediction_mode(&self) -> PredictionMode {
12478        self.prediction_mode
12479    }
12480
12481    fn set_prediction_mode(&mut self, mode: PredictionMode) {
12482        self.prediction_mode = mode;
12483    }
12484}
12485
12486#[cfg(test)]
12487#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
12488mod tests {
12489    use super::*;
12490    use crate::atn::parser::{
12491        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
12492    };
12493    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
12494    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
12495    use crate::token_stream::CommonTokenStream;
12496    use crate::tree::{NodeKind, ParseTreeStats};
12497    use crate::vocabulary::Vocabulary;
12498    use std::cell::RefCell;
12499    use std::mem::size_of;
12500    use std::rc::Rc;
12501    use std::sync::{Arc, Mutex};
12502
12503    #[test]
12504    fn fx_hasher_write_matches_typed_methods_for_full_words() {
12505        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
12506        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
12507        // fields) must hash the same as the typed methods, otherwise an
12508        // `FxHashMap` keyed on such a type silently disagrees with itself
12509        // depending on which entry point the caller used. Verify the
12510        // little-endian word equivalence this PR established.
12511        let value: u64 = 0x0102_0304_0506_0708;
12512        let mut typed = FxHasher::default();
12513        typed.write_u64(value);
12514        let mut bytewise = FxHasher::default();
12515        bytewise.write(&value.to_le_bytes());
12516        assert_eq!(typed.finish(), bytewise.finish());
12517    }
12518
12519    #[derive(Clone, Debug)]
12520    struct TestToken {
12521        spec: TokenSpec,
12522        id: TokenId,
12523        source_name: String,
12524    }
12525
12526    impl TestToken {
12527        fn new(token_type: i32) -> Self {
12528            Self {
12529                spec: TokenSpec::explicit(token_type, ""),
12530                id: TokenId::try_from(0).expect("zero token ID"),
12531                source_name: String::new(),
12532            }
12533        }
12534
12535        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
12536            Self {
12537                spec: TokenSpec::eof(index, index, line, column),
12538                id: TokenId::try_from(0).expect("zero token ID"),
12539                source_name: source_name.to_owned(),
12540            }
12541        }
12542
12543        fn with_text(mut self, text: impl Into<String>) -> Self {
12544            self.spec.text = Some(text.into());
12545            self
12546        }
12547
12548        const fn with_channel(mut self, channel: i32) -> Self {
12549            self.spec.channel = channel;
12550            self
12551        }
12552
12553        const fn with_span(mut self, start: usize, stop: usize) -> Self {
12554            self.spec.start = start;
12555            self.spec.stop = stop;
12556            self.spec.start_byte = start;
12557            self.spec.stop_byte = match stop.checked_add(1) {
12558                Some(end) if end >= start => end,
12559                Some(_) | None => start,
12560            };
12561            self
12562        }
12563
12564        const fn with_position(mut self, line: usize, column: usize) -> Self {
12565            self.spec.line = line;
12566            self.spec.column = column;
12567            self
12568        }
12569
12570        fn set_token_index(&mut self, index: isize) {
12571            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
12572        }
12573    }
12574
12575    impl Token for TestToken {
12576        fn token_id(&self) -> TokenId {
12577            self.id
12578        }
12579
12580        fn token_type(&self) -> i32 {
12581            self.spec.token_type
12582        }
12583
12584        fn channel(&self) -> i32 {
12585            self.spec.channel
12586        }
12587
12588        fn start(&self) -> usize {
12589            self.spec.start
12590        }
12591
12592        fn stop(&self) -> usize {
12593            self.spec.stop
12594        }
12595
12596        fn line(&self) -> usize {
12597            self.spec.line
12598        }
12599
12600        fn column(&self) -> usize {
12601            self.spec.column
12602        }
12603
12604        fn text(&self) -> Option<&str> {
12605            self.spec.text.as_deref()
12606        }
12607
12608        fn source_name(&self) -> &str {
12609            &self.source_name
12610        }
12611
12612        fn start_byte(&self) -> usize {
12613            self.spec.start_byte
12614        }
12615
12616        fn stop_byte(&self) -> usize {
12617            self.spec.stop_byte
12618        }
12619    }
12620
12621    #[derive(Debug)]
12622    struct Source {
12623        tokens: Vec<TestToken>,
12624        index: usize,
12625    }
12626
12627    impl TokenSource for Source {
12628        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12629            let token = self
12630                .tokens
12631                .get(self.index)
12632                .cloned()
12633                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
12634            self.index += 1;
12635            sink.push(token.spec)
12636        }
12637
12638        fn line(&self) -> usize {
12639            1
12640        }
12641
12642        fn column(&self) -> usize {
12643            self.index
12644        }
12645
12646        fn source_name(&self) -> &'static str {
12647            "parser-test"
12648        }
12649    }
12650
12651    #[derive(Clone, Debug, Eq, PartialEq)]
12652    struct RecordedDiagnostic {
12653        grammar_file_name: String,
12654        line: usize,
12655        column: usize,
12656        message: String,
12657        error: Option<AntlrError>,
12658    }
12659
12660    #[derive(Clone, Debug)]
12661    struct RecordingErrorListener {
12662        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
12663    }
12664
12665    impl<R> crate::ErrorListener<R> for RecordingErrorListener
12666    where
12667        R: Recognizer + ?Sized,
12668    {
12669        fn syntax_error(
12670            &mut self,
12671            recognizer: &R,
12672            line: usize,
12673            column: usize,
12674            message: &str,
12675            error: Option<&AntlrError>,
12676        ) {
12677            self.diagnostics
12678                .lock()
12679                .expect("recorded diagnostics lock")
12680                .push(RecordedDiagnostic {
12681                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
12682                    line,
12683                    column,
12684                    message: message.to_owned(),
12685                    error: error.cloned(),
12686                });
12687        }
12688    }
12689
12690    #[derive(Debug)]
12691    struct ReportingSource {
12692        source: Source,
12693        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
12694    }
12695
12696    impl TokenSource for ReportingSource {
12697        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12698            self.source.next_token(sink)
12699        }
12700
12701        fn line(&self) -> usize {
12702            self.source.line()
12703        }
12704
12705        fn column(&self) -> usize {
12706            self.source.column()
12707        }
12708
12709        fn source_name(&self) -> &str {
12710            self.source.source_name()
12711        }
12712
12713        fn report_error(&self, error: &TokenSourceError) -> bool {
12714            self.diagnostics.borrow_mut().push(error.clone());
12715            true
12716        }
12717    }
12718
12719    fn mini_parser_data() -> RecognizerData {
12720        RecognizerData::new(
12721            "Mini.g4",
12722            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12723        )
12724        .with_rule_names(["s"])
12725    }
12726
12727    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
12728        let data = mini_parser_data();
12729        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
12730    }
12731
12732    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
12733    where
12734        H: SemanticHooks,
12735    {
12736        BaseParser::with_semantic_hooks(
12737            CommonTokenStream::new(Source { tokens, index: 0 }),
12738            mini_parser_data(),
12739            hooks,
12740        )
12741    }
12742
12743    #[test]
12744    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
12745        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
12746        parser.remove_error_listeners();
12747        let diagnostics = Arc::new(Mutex::new(Vec::new()));
12748        parser.add_error_listener(RecordingErrorListener {
12749            diagnostics: Arc::clone(&diagnostics),
12750        });
12751        let parser_diagnostics = [ParserDiagnostic {
12752            line: 1,
12753            column: 2,
12754            message: "missing 'x' at 'y'".to_owned(),
12755        }];
12756        let token_errors = [
12757            TokenSourceError::new(1, 1, "token recognition error at: '@'"),
12758            TokenSourceError::new(1, 3, "token recognition error at: '#'"),
12759        ];
12760
12761        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12762
12763        // The interleaved token/parser diagnostic stream (ordering, columns, messages) is one
12764        // reviewable snapshot instead of three hand-written RecordedDiagnostic literals.
12765        insta::assert_debug_snapshot!(
12766            "parser_dispatches_recovery_diagnostics_through_registered_listeners",
12767            *diagnostics.lock().expect("recorded diagnostics lock")
12768        );
12769
12770        parser.remove_error_listeners();
12771        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12772        assert_eq!(
12773            diagnostics.lock().expect("recorded diagnostics lock").len(),
12774            3
12775        );
12776    }
12777
12778    #[test]
12779    fn parser_leaves_token_errors_to_source_owned_listeners() {
12780        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
12781        let source = ReportingSource {
12782            source: Source {
12783                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
12784                index: 0,
12785            },
12786            diagnostics: Rc::clone(&source_diagnostics),
12787        };
12788        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
12789        parser.remove_error_listeners();
12790        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
12791        parser.add_error_listener(RecordingErrorListener {
12792            diagnostics: Arc::clone(&parser_diagnostics),
12793        });
12794        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
12795
12796        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
12797
12798        assert_eq!(*source_diagnostics.borrow(), [source_error]);
12799        assert!(
12800            parser_diagnostics
12801                .lock()
12802                .expect("recorded diagnostics lock")
12803                .is_empty()
12804        );
12805    }
12806
12807    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
12808        builder.finish().expect("valid packed parser ATN")
12809    }
12810
12811    fn nested_rule_chain_atn(depth: usize) -> Atn {
12812        nested_rule_graph_atn(depth, false, false)
12813    }
12814
12815    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
12816        assert!(depth > 0);
12817        let mut atn = ParserAtnBuilder::new(2);
12818        let mut starts = Vec::with_capacity(depth);
12819        let mut stops = Vec::with_capacity(depth);
12820        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
12821        for rule_index in 0..depth {
12822            starts.push(
12823                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
12824                    .expect("rule start")
12825                    .index(),
12826            );
12827        }
12828        for rule_index in 0..depth {
12829            stops.push(
12830                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
12831                    .expect("rule stop")
12832                    .index(),
12833            );
12834        }
12835        if consuming_follows {
12836            for rule_index in 0..depth - 1 {
12837                follows.push(
12838                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
12839                        .expect("rule follow")
12840                        .index(),
12841                );
12842            }
12843        }
12844        atn.set_rule_to_start_state(starts.clone())
12845            .expect("rule start states");
12846        atn.set_rule_to_stop_state(stops.clone())
12847            .expect("rule stop states");
12848        for rule_index in 0..depth - 1 {
12849            let follow_state = if consuming_follows {
12850                follows[rule_index]
12851            } else {
12852                stops[rule_index]
12853            };
12854            atn.add_transition(
12855                starts[rule_index],
12856                ParserTransitionSpec::Rule {
12857                    target: starts[rule_index + 1],
12858                    rule_index: rule_index + 1,
12859                    follow_state,
12860                    precedence: 0,
12861                },
12862            )
12863            .expect("nested rule transition");
12864            if branching {
12865                atn.add_transition(
12866                    starts[rule_index],
12867                    ParserTransitionSpec::Atom {
12868                        target: stops[rule_index],
12869                        label: 2,
12870                    },
12871                )
12872                .expect("dead branch transition");
12873            }
12874            if consuming_follows {
12875                atn.add_transition(
12876                    follow_state,
12877                    ParserTransitionSpec::Atom {
12878                        target: stops[rule_index],
12879                        label: 1,
12880                    },
12881                )
12882                .expect("consuming follow transition");
12883            }
12884        }
12885        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
12886        atn.add_transition(
12887            starts[depth - 1],
12888            ParserTransitionSpec::Set {
12889                target: stops[depth - 1],
12890                set: token_set,
12891            },
12892        )
12893        .expect("terminal set transition");
12894        if branching {
12895            atn.add_transition(
12896                starts[depth - 1],
12897                ParserTransitionSpec::Atom {
12898                    target: stops[depth - 1],
12899                    label: 2,
12900                },
12901            )
12902            .expect("dead leaf branch transition");
12903        }
12904        finish_atn(atn)
12905    }
12906
12907    fn ordinary_star_loop_atn() -> Atn {
12908        let mut atn = ParserAtnBuilder::new(2);
12909        for (state_number, kind, rule_index) in [
12910            (0, AtnStateKind::RuleStart, 0),
12911            (1, AtnStateKind::StarLoopEntry, 0),
12912            (2, AtnStateKind::Basic, 0),
12913            (3, AtnStateKind::StarLoopBack, 0),
12914            (4, AtnStateKind::LoopEnd, 0),
12915            (5, AtnStateKind::Basic, 0),
12916            (6, AtnStateKind::RuleStop, 0),
12917            (7, AtnStateKind::RuleStart, 1),
12918            (8, AtnStateKind::Basic, 1),
12919            (9, AtnStateKind::RuleStop, 1),
12920        ] {
12921            assert_eq!(
12922                atn.add_state(kind, Some(rule_index))
12923                    .expect("state")
12924                    .index(),
12925                state_number
12926            );
12927        }
12928        atn.set_rule_to_start_state(vec![0, 7])
12929            .expect("rule start states");
12930        atn.set_rule_to_stop_state(vec![6, 9])
12931            .expect("rule stop states");
12932        atn.add_decision_state(1).expect("decision state");
12933        atn.set_loop_back_state(4, 3).expect("loop back state");
12934        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12935            .expect("transition");
12936        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12937            .expect("transition");
12938        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12939            .expect("transition");
12940        atn.add_transition(
12941            2,
12942            ParserTransitionSpec::Rule {
12943                target: 7,
12944                rule_index: 1,
12945                follow_state: 3,
12946                precedence: 0,
12947            },
12948        )
12949        .expect("transition");
12950        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12951            .expect("transition");
12952        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12953            .expect("transition");
12954        atn.add_transition(
12955            5,
12956            ParserTransitionSpec::Atom {
12957                target: 6,
12958                label: TOKEN_EOF,
12959            },
12960        )
12961        .expect("transition");
12962        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12963            .expect("transition");
12964        atn.add_transition(
12965            8,
12966            ParserTransitionSpec::Atom {
12967                target: 9,
12968                label: 1,
12969            },
12970        )
12971        .expect("transition");
12972        finish_atn(atn)
12973    }
12974
12975    /// ATN for `s : (X | X X)* EOF`.
12976    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12977        let mut atn = ParserAtnBuilder::new(1);
12978        for (state_number, kind) in [
12979            (0, AtnStateKind::RuleStart),
12980            (1, AtnStateKind::StarLoopEntry),
12981            (2, AtnStateKind::StarBlockStart),
12982            (3, AtnStateKind::Basic),
12983            (4, AtnStateKind::BlockEnd),
12984            (5, AtnStateKind::StarLoopBack),
12985            (6, AtnStateKind::LoopEnd),
12986            (7, AtnStateKind::Basic),
12987            (8, AtnStateKind::RuleStop),
12988        ] {
12989            assert_eq!(
12990                atn.add_state(kind, Some(0)).expect("state").index(),
12991                state_number
12992            );
12993        }
12994        atn.set_rule_to_start_state(vec![0])
12995            .expect("rule start states");
12996        atn.set_rule_to_stop_state(vec![8])
12997            .expect("rule stop states");
12998        atn.set_end_state(2, 4).expect("block end state");
12999        atn.set_loop_back_state(6, 5).expect("loop back state");
13000        atn.add_decision_state(1).expect("decision state");
13001        atn.add_decision_state(2).expect("decision state");
13002        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13003            .expect("transition");
13004        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13005            .expect("transition");
13006        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
13007            .expect("transition");
13008        atn.add_transition(
13009            2,
13010            ParserTransitionSpec::Atom {
13011                target: 4,
13012                label: 1,
13013            },
13014        )
13015        .expect("transition");
13016        atn.add_transition(
13017            2,
13018            ParserTransitionSpec::Atom {
13019                target: 3,
13020                label: 1,
13021            },
13022        )
13023        .expect("transition");
13024        atn.add_transition(
13025            3,
13026            ParserTransitionSpec::Atom {
13027                target: 4,
13028                label: 1,
13029            },
13030        )
13031        .expect("transition");
13032        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13033            .expect("transition");
13034        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
13035            .expect("transition");
13036        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13037            .expect("transition");
13038        atn.add_transition(
13039            7,
13040            ParserTransitionSpec::Atom {
13041                target: 8,
13042                label: TOKEN_EOF,
13043            },
13044        )
13045        .expect("transition");
13046        finish_atn(atn)
13047    }
13048
13049    fn ordinary_plus_loop_atn() -> Atn {
13050        let mut atn = ParserAtnBuilder::new(2);
13051        for (state_number, kind, rule_index) in [
13052            (0, AtnStateKind::RuleStart, 0),
13053            (1, AtnStateKind::Basic, 0),
13054            (2, AtnStateKind::PlusLoopBack, 0),
13055            (3, AtnStateKind::LoopEnd, 0),
13056            (4, AtnStateKind::Basic, 0),
13057            (5, AtnStateKind::RuleStop, 0),
13058            (6, AtnStateKind::RuleStart, 1),
13059            (7, AtnStateKind::Basic, 1),
13060            (8, AtnStateKind::RuleStop, 1),
13061        ] {
13062            assert_eq!(
13063                atn.add_state(kind, Some(rule_index))
13064                    .expect("state")
13065                    .index(),
13066                state_number
13067            );
13068        }
13069        atn.set_rule_to_start_state(vec![0, 6])
13070            .expect("rule start states");
13071        atn.set_rule_to_stop_state(vec![5, 8])
13072            .expect("rule stop states");
13073        atn.add_decision_state(2).expect("decision state");
13074        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13075            .expect("transition");
13076        atn.add_transition(
13077            1,
13078            ParserTransitionSpec::Rule {
13079                target: 6,
13080                rule_index: 1,
13081                follow_state: 2,
13082                precedence: 0,
13083            },
13084        )
13085        .expect("transition");
13086        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
13087            .expect("transition");
13088        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13089            .expect("transition");
13090        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13091            .expect("transition");
13092        atn.add_transition(
13093            4,
13094            ParserTransitionSpec::Atom {
13095                target: 5,
13096                label: TOKEN_EOF,
13097            },
13098        )
13099        .expect("transition");
13100        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13101            .expect("transition");
13102        atn.add_transition(
13103            7,
13104            ParserTransitionSpec::Atom {
13105                target: 8,
13106                label: 1,
13107            },
13108        )
13109        .expect("transition");
13110        finish_atn(atn)
13111    }
13112
13113    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
13114        let mut tokens = (0..count)
13115            .map(|_| TestToken::new(1).with_text("x"))
13116            .collect::<Vec<_>>();
13117        tokens.push(TestToken::eof("parser-test", count, 1, count));
13118        tokens
13119    }
13120
13121    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
13122        let mut atn = ParserAtnBuilder::new(2);
13123        assert_eq!(
13124            atn.add_state(AtnStateKind::RuleStart, Some(0))
13125                .expect("state")
13126                .index(),
13127            0
13128        );
13129        assert_eq!(
13130            atn.add_state(AtnStateKind::Basic, Some(0))
13131                .expect("state")
13132                .index(),
13133            1
13134        );
13135        assert_eq!(
13136            atn.add_state(AtnStateKind::Basic, Some(0))
13137                .expect("state")
13138                .index(),
13139            2
13140        );
13141        assert_eq!(
13142            atn.add_state(AtnStateKind::RuleStart, Some(1))
13143                .expect("state")
13144                .index(),
13145            3
13146        );
13147        atn.set_left_recursive_rule(3)
13148            .expect("left-recursive rule start");
13149        assert_eq!(
13150            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
13151                .expect("state")
13152                .index(),
13153            4
13154        );
13155        atn.set_precedence_rule_decision(4)
13156            .expect("precedence decision");
13157        assert_eq!(
13158            atn.add_state(AtnStateKind::Basic, Some(1))
13159                .expect("state")
13160                .index(),
13161            5
13162        );
13163        assert_eq!(
13164            atn.add_state(AtnStateKind::Basic, Some(1))
13165                .expect("state")
13166                .index(),
13167            6
13168        );
13169        assert_eq!(
13170            atn.add_state(AtnStateKind::LoopEnd, Some(1))
13171                .expect("state")
13172                .index(),
13173            7
13174        );
13175        assert_eq!(
13176            atn.add_state(AtnStateKind::RuleStop, Some(1))
13177                .expect("state")
13178                .index(),
13179            8
13180        );
13181        assert_eq!(
13182            atn.add_state(AtnStateKind::RuleStop, Some(0))
13183                .expect("state")
13184                .index(),
13185            9
13186        );
13187        atn.set_rule_to_start_state(vec![0, 3])
13188            .expect("rule start states");
13189        atn.set_rule_to_stop_state(vec![9, 8])
13190            .expect("rule stop states");
13191        atn.add_transition(
13192            1,
13193            ParserTransitionSpec::Rule {
13194                target: 3,
13195                rule_index: 1,
13196                follow_state: 2,
13197                precedence: 0,
13198            },
13199        )
13200        .expect("transition");
13201        atn.add_transition(
13202            2,
13203            ParserTransitionSpec::Atom {
13204                target: 9,
13205                label: caller_symbol,
13206            },
13207        )
13208        .expect("transition");
13209        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13210            .expect("transition");
13211        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
13212            .expect("transition");
13213        atn.add_transition(
13214            5,
13215            ParserTransitionSpec::Precedence {
13216                target: 6,
13217                precedence: 1,
13218            },
13219        )
13220        .expect("transition");
13221        atn.add_transition(
13222            6,
13223            ParserTransitionSpec::Atom {
13224                target: 4,
13225                label: 1,
13226            },
13227        )
13228        .expect("transition");
13229        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13230            .expect("transition");
13231        finish_atn(atn)
13232    }
13233
13234    fn labeled_left_recursive_operator_atn() -> Atn {
13235        let mut atn = ParserAtnBuilder::new(4);
13236        for (state, kind) in [
13237            (0, AtnStateKind::RuleStart),
13238            (1, AtnStateKind::BlockStart),
13239            (2, AtnStateKind::StarLoopEntry),
13240            (3, AtnStateKind::StarBlockStart),
13241            (4, AtnStateKind::Basic),
13242            (5, AtnStateKind::Basic),
13243            (6, AtnStateKind::Basic),
13244            (7, AtnStateKind::StarLoopBack),
13245            (8, AtnStateKind::LoopEnd),
13246            (9, AtnStateKind::RuleStop),
13247        ] {
13248            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13249        }
13250        atn.set_left_recursive_rule(0)
13251            .expect("left-recursive rule start");
13252        atn.set_precedence_rule_decision(2)
13253            .expect("precedence decision");
13254        atn.set_loop_back_state(8, 7).expect("loop-back state");
13255        atn.set_rule_to_start_state(vec![0])
13256            .expect("rule start states");
13257        atn.set_rule_to_stop_state(vec![9])
13258            .expect("rule stop states");
13259        for state in [1, 2, 3] {
13260            atn.add_decision_state(state).expect("decision state");
13261        }
13262        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
13263            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
13264                .expect("epsilon transition");
13265        }
13266        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
13267            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
13268                .expect("token transition");
13269        }
13270        for (target, precedence) in [(4, 2), (5, 1)] {
13271            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
13272                .expect("operator precedence");
13273        }
13274        finish_atn(atn)
13275    }
13276
13277    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
13278        let mut parser = mini_parser(vec![
13279            TestToken::new(symbol).with_text("lookahead"),
13280            TestToken::eof("parser-test", 1, 1, 1),
13281        ]);
13282        parser.rule_context_stack = vec![
13283            RuleContextFrame {
13284                rule_index: 0,
13285                invoking_state: -1,
13286            },
13287            RuleContextFrame {
13288                rule_index: 1,
13289                invoking_state: 1,
13290            },
13291        ];
13292        parser
13293    }
13294
13295    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
13296        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
13297        //   prec 2: token 1, token 1  (shift `>>`)
13298        //   prec 1: token 1           (relational `>`)
13299        let mut atn = ParserAtnBuilder::new(1);
13300        for (state, kind, rule) in [
13301            (0, AtnStateKind::RuleStart, 0),
13302            (1, AtnStateKind::StarLoopEntry, 0),
13303            (2, AtnStateKind::Basic, 0), // ops hub
13304            (3, AtnStateKind::Basic, 0), // shift prec
13305            (4, AtnStateKind::Basic, 0), // shift first >
13306            (5, AtnStateKind::Basic, 0), // shift second >
13307            (6, AtnStateKind::Basic, 0), // rel prec
13308            (7, AtnStateKind::Basic, 0), // rel >
13309            (8, AtnStateKind::LoopEnd, 0),
13310            (9, AtnStateKind::RuleStop, 0),
13311        ] {
13312            assert_eq!(
13313                atn.add_state(kind, Some(rule)).expect("state").index(),
13314                state
13315            );
13316            if state == 0 {
13317                atn.set_left_recursive_rule(state)
13318                    .expect("left-recursive rule start");
13319            } else if state == 1 {
13320                atn.set_precedence_rule_decision(state)
13321                    .expect("precedence decision");
13322            }
13323        }
13324        atn.set_rule_to_start_state(vec![0])
13325            .expect("rule start states");
13326        atn.set_rule_to_stop_state(vec![9])
13327            .expect("rule stop states");
13328        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13329            .expect("ops");
13330        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13331            .expect("exit");
13332        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13333            .expect("to shift");
13334        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13335            .expect("to rel");
13336        atn.add_transition(
13337            3,
13338            ParserTransitionSpec::Precedence {
13339                target: 4,
13340                precedence: 2,
13341            },
13342        )
13343        .expect("shift prec");
13344        atn.add_transition(
13345            4,
13346            ParserTransitionSpec::Atom {
13347                target: 5,
13348                label: 1,
13349            },
13350        )
13351        .expect("shift first >");
13352        atn.add_transition(
13353            5,
13354            ParserTransitionSpec::Atom {
13355                target: 1,
13356                label: 1,
13357            },
13358        )
13359        .expect("shift second >");
13360        atn.add_transition(
13361            6,
13362            ParserTransitionSpec::Precedence {
13363                target: 7,
13364                precedence: 1,
13365            },
13366        )
13367        .expect("rel prec");
13368        atn.add_transition(
13369            7,
13370            ParserTransitionSpec::Atom {
13371                target: 1,
13372                label: 1,
13373            },
13374        )
13375        .expect("rel >");
13376        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13377            .expect("loop end");
13378        finish_atn(atn)
13379    }
13380
13381    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
13382        let mut atn = ParserAtnBuilder::new(2);
13383        for (state, kind, rule) in [
13384            (0, AtnStateKind::RuleStart, 0),
13385            (1, AtnStateKind::StarLoopEntry, 0),
13386            (2, AtnStateKind::Basic, 0),
13387            (3, AtnStateKind::Basic, 0),
13388            (4, AtnStateKind::Basic, 0),
13389            (5, AtnStateKind::Basic, 0),
13390            (6, AtnStateKind::Basic, 0),
13391            (7, AtnStateKind::Basic, 0),
13392            (8, AtnStateKind::LoopEnd, 0),
13393            (9, AtnStateKind::RuleStop, 0),
13394            (10, AtnStateKind::RuleStart, 1),
13395            (11, AtnStateKind::Basic, 1),
13396            (12, AtnStateKind::RuleStop, 1),
13397        ] {
13398            assert_eq!(
13399                atn.add_state(kind, Some(rule)).expect("state").index(),
13400                state
13401            );
13402            if state == 0 {
13403                atn.set_left_recursive_rule(state)
13404                    .expect("left-recursive rule start");
13405            } else if state == 1 {
13406                atn.set_precedence_rule_decision(state)
13407                    .expect("precedence decision");
13408            }
13409        }
13410        atn.set_rule_to_start_state(vec![0, 10])
13411            .expect("rule start states");
13412        atn.set_rule_to_stop_state(vec![9, 12])
13413            .expect("rule stop states");
13414        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13415            .expect("ops");
13416        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13417            .expect("exit");
13418        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13419            .expect("to shift");
13420        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13421            .expect("to relational");
13422        atn.add_transition(
13423            3,
13424            ParserTransitionSpec::Precedence {
13425                target: 4,
13426                precedence: 2,
13427            },
13428        )
13429        .expect("shift precedence");
13430        atn.add_transition(
13431            4,
13432            ParserTransitionSpec::Rule {
13433                target: 10,
13434                rule_index: 1,
13435                follow_state: 5,
13436                precedence: 0,
13437            },
13438        )
13439        .expect("first shift token helper");
13440        atn.add_transition(
13441            5,
13442            ParserTransitionSpec::Atom {
13443                target: 1,
13444                label: 1,
13445            },
13446        )
13447        .expect("second shift token");
13448        atn.add_transition(
13449            6,
13450            ParserTransitionSpec::Precedence {
13451                target: 7,
13452                precedence: 1,
13453            },
13454        )
13455        .expect("relational precedence");
13456        atn.add_transition(
13457            7,
13458            ParserTransitionSpec::Atom {
13459                target: 1,
13460                label: 1,
13461            },
13462        )
13463        .expect("relational token");
13464        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13465            .expect("loop end");
13466        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13467            .expect("helper entry");
13468        atn.add_transition(
13469            11,
13470            ParserTransitionSpec::Atom {
13471                target: 12,
13472                label: 1,
13473            },
13474        )
13475        .expect("first shift token");
13476        finish_atn(atn)
13477    }
13478
13479    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
13480        let mut atn = ParserAtnBuilder::new(1);
13481        for (state, kind) in [
13482            (0, AtnStateKind::RuleStart),
13483            (1, AtnStateKind::StarLoopEntry),
13484            (2, AtnStateKind::Basic),
13485            (3, AtnStateKind::Basic),
13486            (4, AtnStateKind::Basic),
13487            (5, AtnStateKind::Basic),
13488            (6, AtnStateKind::Basic),
13489            (7, AtnStateKind::Basic),
13490            (8, AtnStateKind::Basic),
13491            (9, AtnStateKind::LoopEnd),
13492            (10, AtnStateKind::RuleStop),
13493        ] {
13494            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13495            if state == 0 {
13496                atn.set_left_recursive_rule(state)
13497                    .expect("left-recursive rule start");
13498            } else if state == 1 {
13499                atn.set_precedence_rule_decision(state)
13500                    .expect("precedence decision");
13501            }
13502        }
13503        atn.set_rule_to_start_state(vec![0])
13504            .expect("rule start states");
13505        atn.set_rule_to_stop_state(vec![10])
13506            .expect("rule stop states");
13507        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13508            .expect("ops");
13509        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
13510            .expect("exit");
13511        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13512            .expect("to multi-token operator");
13513        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13514            .expect("to predicate operator");
13515        atn.add_transition(
13516            3,
13517            ParserTransitionSpec::Precedence {
13518                target: 4,
13519                precedence: 2,
13520            },
13521        )
13522        .expect("multi-token precedence");
13523        atn.add_transition(
13524            4,
13525            ParserTransitionSpec::Atom {
13526                target: 5,
13527                label: 1,
13528            },
13529        )
13530        .expect("multi-token first");
13531        atn.add_transition(
13532            5,
13533            ParserTransitionSpec::Atom {
13534                target: 1,
13535                label: 1,
13536            },
13537        )
13538        .expect("multi-token second");
13539        atn.add_transition(
13540            6,
13541            ParserTransitionSpec::Precedence {
13542                target: 7,
13543                precedence: 2,
13544            },
13545        )
13546        .expect("predicate precedence");
13547        atn.add_transition(
13548            7,
13549            ParserTransitionSpec::Predicate {
13550                target: 8,
13551                rule_index: 0,
13552                pred_index: 0,
13553                context_dependent: false,
13554            },
13555        )
13556        .expect("operator predicate");
13557        atn.add_transition(
13558            8,
13559            ParserTransitionSpec::Atom {
13560                target: 1,
13561                label: 1,
13562            },
13563        )
13564        .expect("predicate single token");
13565        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13566            .expect("loop end");
13567        finish_atn(atn)
13568    }
13569
13570    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
13571        let mut atn = ParserAtnBuilder::new(2);
13572        for (state, kind, rule) in [
13573            (0, AtnStateKind::RuleStart, 0),
13574            (1, AtnStateKind::StarLoopEntry, 0),
13575            (2, AtnStateKind::Basic, 0),
13576            (3, AtnStateKind::Basic, 0),
13577            (4, AtnStateKind::Basic, 0),
13578            (5, AtnStateKind::LoopEnd, 0),
13579            (6, AtnStateKind::RuleStop, 0),
13580            (7, AtnStateKind::RuleStart, 1),
13581            (8, AtnStateKind::RuleStop, 1),
13582            (9, AtnStateKind::Basic, 1),
13583        ] {
13584            assert_eq!(
13585                atn.add_state(kind, Some(rule)).expect("state").index(),
13586                state
13587            );
13588            if state == 0 {
13589                atn.set_left_recursive_rule(state)
13590                    .expect("left-recursive rule start");
13591            } else if state == 1 {
13592                atn.set_precedence_rule_decision(state)
13593                    .expect("precedence decision");
13594            }
13595        }
13596        atn.set_rule_to_start_state(vec![0, 7])
13597            .expect("rule start states");
13598        atn.set_rule_to_stop_state(vec![6, 8])
13599            .expect("rule stop states");
13600        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13601            .expect("transition");
13602        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13603            .expect("transition");
13604        atn.add_transition(
13605            2,
13606            ParserTransitionSpec::Precedence {
13607                target: 3,
13608                precedence: 3,
13609            },
13610        )
13611        .expect("transition");
13612        atn.add_transition(
13613            3,
13614            ParserTransitionSpec::Rule {
13615                target: 7,
13616                rule_index: 1,
13617                follow_state: 4,
13618                precedence: 0,
13619            },
13620        )
13621        .expect("transition");
13622        atn.add_transition(
13623            4,
13624            ParserTransitionSpec::Atom {
13625                target: 1,
13626                label: 1,
13627            },
13628        )
13629        .expect("transition");
13630        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13631            .expect("transition");
13632        atn.add_transition(
13633            7,
13634            ParserTransitionSpec::Precedence {
13635                target: 9,
13636                precedence: 1,
13637            },
13638        )
13639        .expect("transition");
13640        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
13641            .expect("transition");
13642        finish_atn(atn)
13643    }
13644
13645    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
13646        let mut atn = ParserAtnBuilder::new(2);
13647        for (state, kind) in [
13648            (0, AtnStateKind::RuleStart),
13649            (1, AtnStateKind::StarLoopEntry),
13650            (2, AtnStateKind::Basic),
13651            (3, AtnStateKind::Basic),
13652            (4, AtnStateKind::Basic),
13653            (5, AtnStateKind::LoopEnd),
13654            (6, AtnStateKind::RuleStop),
13655        ] {
13656            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13657            if state == 0 {
13658                atn.set_left_recursive_rule(state)
13659                    .expect("left-recursive rule start");
13660            } else if state == 1 {
13661                atn.set_precedence_rule_decision(state)
13662                    .expect("precedence decision");
13663            }
13664        }
13665        atn.set_rule_to_start_state(vec![0])
13666            .expect("rule start states");
13667        atn.set_rule_to_stop_state(vec![6])
13668            .expect("rule stop states");
13669        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13670            .expect("transition");
13671        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13672            .expect("transition");
13673        atn.add_transition(
13674            2,
13675            ParserTransitionSpec::Precedence {
13676                target: 3,
13677                precedence: 1,
13678            },
13679        )
13680        .expect("transition");
13681        atn.add_transition(
13682            3,
13683            ParserTransitionSpec::Predicate {
13684                target: 4,
13685                rule_index: 0,
13686                pred_index: 0,
13687                context_dependent: false,
13688            },
13689        )
13690        .expect("transition");
13691        atn.add_transition(
13692            4,
13693            ParserTransitionSpec::Atom {
13694                target: 1,
13695                label: 1,
13696            },
13697        )
13698        .expect("transition");
13699        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13700            .expect("transition");
13701        finish_atn(atn)
13702    }
13703
13704    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
13705        let mut atn = ParserAtnBuilder::new(2);
13706        for (state, kind, rule) in [
13707            (0, AtnStateKind::RuleStart, 0),
13708            (1, AtnStateKind::Basic, 0),
13709            (2, AtnStateKind::Basic, 0),
13710            (3, AtnStateKind::Basic, 0),
13711            (4, AtnStateKind::RuleStop, 0),
13712            (5, AtnStateKind::RuleStart, 1),
13713            (6, AtnStateKind::StarLoopEntry, 1),
13714            (7, AtnStateKind::Basic, 1),
13715            (8, AtnStateKind::Basic, 1),
13716            (9, AtnStateKind::LoopEnd, 1),
13717            (10, AtnStateKind::RuleStop, 1),
13718            (11, AtnStateKind::RuleStart, 2),
13719            (12, AtnStateKind::RuleStop, 2),
13720        ] {
13721            assert_eq!(
13722                atn.add_state(kind, Some(rule)).expect("state").index(),
13723                state
13724            );
13725            if state == 5 {
13726                atn.set_left_recursive_rule(state)
13727                    .expect("left-recursive rule start");
13728            } else if state == 6 {
13729                atn.set_precedence_rule_decision(state)
13730                    .expect("precedence decision");
13731            }
13732        }
13733        atn.set_rule_to_start_state(vec![0, 5, 11])
13734            .expect("rule start states");
13735        atn.set_rule_to_stop_state(vec![4, 10, 12])
13736            .expect("rule stop states");
13737        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13738            .expect("transition");
13739        atn.add_transition(
13740            1,
13741            ParserTransitionSpec::Rule {
13742                target: 5,
13743                rule_index: 1,
13744                follow_state: 2,
13745                precedence: 0,
13746            },
13747        )
13748        .expect("transition");
13749        atn.add_transition(
13750            2,
13751            ParserTransitionSpec::Rule {
13752                target: 11,
13753                rule_index: 2,
13754                follow_state: 3,
13755                precedence: 0,
13756            },
13757        )
13758        .expect("transition");
13759        atn.add_transition(
13760            3,
13761            ParserTransitionSpec::Atom {
13762                target: 4,
13763                label: caller_symbol,
13764            },
13765        )
13766        .expect("transition");
13767        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13768            .expect("transition");
13769        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
13770            .expect("transition");
13771        atn.add_transition(
13772            7,
13773            ParserTransitionSpec::Precedence {
13774                target: 8,
13775                precedence: 1,
13776            },
13777        )
13778        .expect("transition");
13779        atn.add_transition(
13780            8,
13781            ParserTransitionSpec::Atom {
13782                target: 6,
13783                label: 1,
13784            },
13785        )
13786        .expect("transition");
13787        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13788            .expect("transition");
13789        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
13790            .expect("transition");
13791        finish_atn(atn)
13792    }
13793
13794    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
13795        let mut atn = ParserAtnBuilder::new(2);
13796        for (state, kind, rule) in [
13797            (0, AtnStateKind::RuleStart, 0),
13798            (1, AtnStateKind::Basic, 0),
13799            (2, AtnStateKind::Basic, 0),
13800            (3, AtnStateKind::RuleStop, 0),
13801            (4, AtnStateKind::RuleStart, 1),
13802            (5, AtnStateKind::Basic, 1),
13803            (6, AtnStateKind::Basic, 1),
13804            (7, AtnStateKind::RuleStop, 1),
13805            (8, AtnStateKind::RuleStart, 2),
13806            (9, AtnStateKind::StarLoopEntry, 2),
13807            (10, AtnStateKind::Basic, 2),
13808            (11, AtnStateKind::Basic, 2),
13809            (12, AtnStateKind::LoopEnd, 2),
13810            (13, AtnStateKind::RuleStop, 2),
13811        ] {
13812            assert_eq!(
13813                atn.add_state(kind, Some(rule)).expect("state").index(),
13814                state
13815            );
13816            if state == 8 {
13817                atn.set_left_recursive_rule(state)
13818                    .expect("left-recursive rule start");
13819            } else if state == 9 {
13820                atn.set_precedence_rule_decision(state)
13821                    .expect("precedence decision");
13822            }
13823        }
13824        atn.set_rule_to_start_state(vec![0, 4, 8])
13825            .expect("rule start states");
13826        atn.set_rule_to_stop_state(vec![3, 7, 13])
13827            .expect("rule stop states");
13828        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13829            .expect("transition");
13830        atn.add_transition(
13831            1,
13832            ParserTransitionSpec::Rule {
13833                target: 4,
13834                rule_index: 1,
13835                follow_state: 2,
13836                precedence: 0,
13837            },
13838        )
13839        .expect("transition");
13840        atn.add_transition(
13841            2,
13842            ParserTransitionSpec::Atom {
13843                target: 3,
13844                label: caller_symbol,
13845            },
13846        )
13847        .expect("transition");
13848        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13849            .expect("transition");
13850        atn.add_transition(
13851            5,
13852            ParserTransitionSpec::Rule {
13853                target: 8,
13854                rule_index: 2,
13855                follow_state: 6,
13856                precedence: 0,
13857            },
13858        )
13859        .expect("transition");
13860        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13861            .expect("transition");
13862        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13863            .expect("transition");
13864        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
13865            .expect("transition");
13866        atn.add_transition(
13867            10,
13868            ParserTransitionSpec::Precedence {
13869                target: 11,
13870                precedence: 1,
13871            },
13872        )
13873        .expect("transition");
13874        atn.add_transition(
13875            11,
13876            ParserTransitionSpec::Atom {
13877                target: 9,
13878                label: 1,
13879            },
13880        )
13881        .expect("transition");
13882        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
13883            .expect("transition");
13884        finish_atn(atn)
13885    }
13886
13887    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
13888        let mut atn = ParserAtnBuilder::new(2);
13889        for (state, kind, rule) in [
13890            (0, AtnStateKind::RuleStart, 0),
13891            (1, AtnStateKind::Basic, 0),
13892            (2, AtnStateKind::Basic, 0),
13893            (3, AtnStateKind::RuleStop, 0),
13894            (4, AtnStateKind::RuleStart, 1),
13895            (5, AtnStateKind::StarLoopEntry, 1),
13896            (6, AtnStateKind::Basic, 1),
13897            (7, AtnStateKind::Basic, 1),
13898            (8, AtnStateKind::Basic, 1),
13899            (9, AtnStateKind::Basic, 1),
13900            (10, AtnStateKind::LoopEnd, 1),
13901            (11, AtnStateKind::RuleStop, 1),
13902        ] {
13903            assert_eq!(
13904                atn.add_state(kind, Some(rule)).expect("state").index(),
13905                state
13906            );
13907            if state == 4 {
13908                atn.set_left_recursive_rule(state)
13909                    .expect("left-recursive rule start");
13910            } else if state == 5 {
13911                atn.set_precedence_rule_decision(state)
13912                    .expect("precedence decision");
13913            }
13914        }
13915        atn.set_rule_to_start_state(vec![0, 4])
13916            .expect("rule start states");
13917        atn.set_rule_to_stop_state(vec![3, 11])
13918            .expect("rule stop states");
13919        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13920            .expect("transition");
13921        atn.add_transition(
13922            1,
13923            ParserTransitionSpec::Rule {
13924                target: 4,
13925                rule_index: 1,
13926                follow_state: 2,
13927                precedence: 0,
13928            },
13929        )
13930        .expect("transition");
13931        atn.add_transition(
13932            2,
13933            ParserTransitionSpec::Atom {
13934                target: 3,
13935                label: caller_symbol,
13936            },
13937        )
13938        .expect("transition");
13939        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13940            .expect("transition");
13941        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
13942            .expect("transition");
13943        atn.add_transition(
13944            6,
13945            ParserTransitionSpec::Precedence {
13946                target: 7,
13947                precedence: 1,
13948            },
13949        )
13950        .expect("transition");
13951        atn.add_transition(
13952            7,
13953            ParserTransitionSpec::Atom {
13954                target: 8,
13955                label: 1,
13956            },
13957        )
13958        .expect("transition");
13959        atn.add_transition(
13960            8,
13961            ParserTransitionSpec::Rule {
13962                target: 4,
13963                rule_index: 1,
13964                follow_state: 9,
13965                precedence: 2,
13966            },
13967        )
13968        .expect("transition");
13969        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13970            .expect("transition");
13971        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13972            .expect("transition");
13973        finish_atn(atn)
13974    }
13975
13976    #[test]
13977    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13978        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13979        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13980
13981        let mut overlapping = parser_inside_left_recursive_callee(1);
13982        assert_eq!(
13983            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13984            None
13985        );
13986
13987        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13988        assert_eq!(
13989            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13990            Some(true)
13991        );
13992
13993        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13994        assert_eq!(
13995            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13996            Some(false)
13997        );
13998
13999        assert_eq!(
14000            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14001            Some(true),
14002            "overlap results must not leak across ATNs"
14003        );
14004    }
14005
14006    #[test]
14007    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
14008        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
14009        let mut parser = mini_parser(vec![
14010            TestToken::new(1).with_text("operator"),
14011            TestToken::eof("parser-test", 1, 1, 1),
14012        ]);
14013        parser.rule_context_stack = vec![RuleContextFrame {
14014            rule_index: 0,
14015            invoking_state: -1,
14016        }];
14017
14018        assert_eq!(
14019            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14020            Some(true)
14021        );
14022        assert_eq!(
14023            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14024            Some(true),
14025            "cached operator lookahead must preserve the nullable prefix return path"
14026        );
14027        assert_eq!(
14028            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14029            Some(true),
14030            "the nullable child must use its rule-call precedence, not the caller precedence"
14031        );
14032    }
14033
14034    #[test]
14035    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
14036        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
14037        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
14038        // must defer so StarLoopEntry adaptive predict can exit when the second
14039        // `>` is absent (as in `a < b > c`).
14040        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
14041        let mut parser = mini_parser(vec![
14042            TestToken::new(1).with_text(">"),
14043            TestToken::new(2).with_text("id"),
14044            TestToken::eof("parser-test", 1, 1, 1),
14045        ]);
14046        parser.rule_context_stack = vec![RuleContextFrame {
14047            rule_index: 0,
14048            invoking_state: -1,
14049        }];
14050
14051        assert_eq!(
14052            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14053            Some(true),
14054            "at low precedence relational `>` is a single-token operator"
14055        );
14056        assert_eq!(
14057            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
14058            Some(true),
14059            "relational remains single-token at its own precedence"
14060        );
14061        assert_eq!(
14062            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14063            None,
14064            "at shift precedence, bare `>` must not force enter"
14065        );
14066    }
14067
14068    #[test]
14069    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
14070        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
14071        let mut parser = mini_parser(vec![
14072            TestToken::new(1).with_text(">"),
14073            TestToken::new(2).with_text("id"),
14074            TestToken::eof("parser-test", 1, 1, 1),
14075        ]);
14076        parser.rule_context_stack = vec![RuleContextFrame {
14077            rule_index: 0,
14078            invoking_state: -1,
14079        }];
14080
14081        assert_eq!(
14082            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14083            Some(true),
14084            "the direct relational alternative remains a one-token operator"
14085        );
14086        assert_eq!(
14087            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14088            None,
14089            "a token matched in the helper rule must return to the second shift token"
14090        );
14091    }
14092
14093    #[test]
14094    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
14095        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
14096        let mut parser = mini_parser(vec![
14097            TestToken::new(1).with_text(">"),
14098            TestToken::new(2).with_text("id"),
14099            TestToken::eof("parser-test", 1, 1, 1),
14100        ]);
14101        parser.rule_context_stack = vec![RuleContextFrame {
14102            rule_index: 0,
14103            invoking_state: -1,
14104        }];
14105
14106        assert_eq!(
14107            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14108            None,
14109            "a predicate-gated single-token path must not be hidden by a multi-token path"
14110        );
14111    }
14112
14113    #[test]
14114    fn left_recursive_loop_defers_predicate_guarded_operator() {
14115        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
14116        let mut parser = mini_parser_with_hooks(
14117            vec![
14118                TestToken::new(1).with_text("operator"),
14119                TestToken::eof("parser-test", 1, 1, 1),
14120            ],
14121            RejectingPredicateHooks::default(),
14122        );
14123        parser.rule_context_stack = vec![RuleContextFrame {
14124            rule_index: 0,
14125            invoking_state: -1,
14126        }];
14127
14128        assert_eq!(
14129            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14130            None,
14131            "a false predicate must be evaluated before entering the operator alternative"
14132        );
14133        assert_eq!(
14134            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14135            None,
14136            "cached predicate-dependent lookahead must keep deferring"
14137        );
14138    }
14139
14140    #[test]
14141    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
14142        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
14143        let mut parser = parser_inside_left_recursive_callee(1);
14144
14145        assert_eq!(
14146            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14147            None
14148        );
14149        assert_eq!(
14150            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14151            None,
14152            "the cached overlap must preserve the nullable child return path"
14153        );
14154    }
14155
14156    #[test]
14157    fn left_recursive_loop_defers_through_nullable_parent_return() {
14158        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
14159        let mut parser = mini_parser(vec![
14160            TestToken::new(1).with_text("lookahead"),
14161            TestToken::eof("parser-test", 1, 1, 1),
14162        ]);
14163        parser.rule_context_stack = vec![
14164            RuleContextFrame {
14165                rule_index: 0,
14166                invoking_state: -1,
14167            },
14168            RuleContextFrame {
14169                rule_index: 1,
14170                invoking_state: 1,
14171            },
14172            RuleContextFrame {
14173                rule_index: 2,
14174                invoking_state: 5,
14175            },
14176        ];
14177
14178        assert_eq!(
14179            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14180            None,
14181            "a nullable caller must unwind to its parent's consuming follow path"
14182        );
14183        assert_eq!(
14184            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14185            None,
14186            "the caller-overlap cache must not retain a false negative"
14187        );
14188    }
14189
14190    #[test]
14191    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
14192        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
14193        let mut parser = mini_parser(vec![
14194            TestToken::new(1).with_text("lookahead"),
14195            TestToken::eof("parser-test", 1, 1, 1),
14196        ]);
14197        parser.rule_context_stack = vec![
14198            RuleContextFrame {
14199                rule_index: 0,
14200                invoking_state: -1,
14201            },
14202            RuleContextFrame {
14203                rule_index: 1,
14204                invoking_state: 1,
14205            },
14206            RuleContextFrame {
14207                rule_index: 1,
14208                invoking_state: 8,
14209            },
14210        ];
14211
14212        assert_eq!(
14213            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14214            None,
14215            "a recursive operand return must preserve its parent caller context"
14216        );
14217        assert_eq!(
14218            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14219            None,
14220            "the caller-overlap cache must preserve the loop-boundary return"
14221        );
14222    }
14223
14224    fn token_then_eof_atn() -> Atn {
14225        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14226            4, 1, 2, // version, parser, max token type
14227            3, // states
14228            2, 0, // rule start
14229            1, 0, // basic
14230            7, 0, // rule stop
14231            0, // non-greedy states
14232            0, // precedence states
14233            1, // rules
14234            0, // rule 0 start
14235            0, // modes
14236            0, // sets
14237            2, // transitions
14238            0, 1, 5, 1, 0, 0, // match token 1
14239            1, 2, 5, -1, 0, 0, // match EOF
14240            0, // decisions
14241        ]))
14242        .deserialize_parser()
14243        .expect("artificial parser ATN should deserialize")
14244    }
14245
14246    fn epsilon_cycle_atn() -> Atn {
14247        let mut atn = ParserAtnBuilder::new(1);
14248        for (state_number, kind) in [
14249            (0, AtnStateKind::RuleStart),
14250            (1, AtnStateKind::Basic),
14251            (2, AtnStateKind::RuleStop),
14252        ] {
14253            assert_eq!(
14254                atn.add_state(kind, Some(0)).expect("state").index(),
14255                state_number
14256            );
14257        }
14258        atn.set_rule_to_start_state(vec![0])
14259            .expect("rule start states");
14260        atn.set_rule_to_stop_state(vec![2])
14261            .expect("rule stop states");
14262        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14263            .expect("transition");
14264        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
14265            .expect("self-cycle transition");
14266        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14267            .expect("exit transition");
14268        finish_atn(atn)
14269    }
14270
14271    fn eof_then_action_atn() -> Atn {
14272        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14273            4, 1, 1, // version, parser, max token type
14274            3, // states
14275            2, 0, // rule start
14276            1, 0, // basic
14277            7, 0, // rule stop
14278            0, // non-greedy states
14279            0, // precedence states
14280            1, // rules
14281            0, // rule 0 start
14282            0, // modes
14283            0, // sets
14284            2, // transitions
14285            0, 1, 5, -1, 0, 0, // match EOF
14286            1, 2, 6, 0, 0, 0, // parser action
14287            0, // decisions
14288        ]))
14289        .deserialize_parser()
14290        .expect("artificial parser ATN should deserialize")
14291    }
14292
14293    fn noop_action_then_token_then_eof_atn() -> Atn {
14294        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14295            4, 1, 2, // version, parser, max token type
14296            4, // states
14297            2, 0, // rule start
14298            1, 0, // basic
14299            1, 0, // basic
14300            7, 0, // rule stop
14301            0, // non-greedy states
14302            0, // precedence states
14303            1, // rules
14304            0, // rule 0 start
14305            0, // modes
14306            0, // sets
14307            3, // transitions
14308            0, 1, 6, 0, -1, 0, // no-op parser action
14309            1, 2, 5, 1, 0, 0, // match token 1
14310            2, 3, 5, -1, 0, 0, // match EOF
14311            0, // decisions
14312        ]))
14313        .deserialize_parser()
14314        .expect("artificial no-op action ATN should deserialize")
14315    }
14316
14317    fn two_alt_decision_atn() -> Atn {
14318        let mut atn = ParserAtnBuilder::new(2);
14319        assert_eq!(
14320            atn.add_state(AtnStateKind::RuleStart, Some(0))
14321                .expect("state")
14322                .index(),
14323            0
14324        );
14325        assert_eq!(
14326            atn.add_state(AtnStateKind::BlockStart, Some(0))
14327                .expect("state")
14328                .index(),
14329            1
14330        );
14331        assert_eq!(
14332            atn.add_state(AtnStateKind::Basic, Some(0))
14333                .expect("state")
14334                .index(),
14335            2
14336        );
14337        assert_eq!(
14338            atn.add_state(AtnStateKind::Basic, Some(0))
14339                .expect("state")
14340                .index(),
14341            3
14342        );
14343        assert_eq!(
14344            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14345                .expect("state")
14346                .index(),
14347            4
14348        );
14349        assert_eq!(
14350            atn.add_state(AtnStateKind::RuleStop, Some(0))
14351                .expect("state")
14352                .index(),
14353            5
14354        );
14355        atn.set_rule_to_start_state(vec![0])
14356            .expect("rule start states");
14357        atn.set_rule_to_stop_state(vec![5])
14358            .expect("rule stop states");
14359        atn.add_decision_state(1).expect("decision state");
14360        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14361            .expect("transition");
14362        atn.add_transition(
14363            1,
14364            ParserTransitionSpec::Atom {
14365                target: 2,
14366                label: 1,
14367            },
14368        )
14369        .expect("transition");
14370        atn.add_transition(
14371            1,
14372            ParserTransitionSpec::Atom {
14373                target: 3,
14374                label: 2,
14375            },
14376        )
14377        .expect("transition");
14378        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
14379            .expect("transition");
14380        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14381            .expect("transition");
14382        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14383            .expect("transition");
14384        finish_atn(atn)
14385    }
14386
14387    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
14388    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
14389    fn optional_then_b_eof_atn() -> Atn {
14390        let mut atn = ParserAtnBuilder::new(3);
14391        assert_eq!(
14392            atn.add_state(AtnStateKind::RuleStart, Some(0))
14393                .expect("state")
14394                .index(),
14395            0
14396        );
14397        assert_eq!(
14398            atn.add_state(AtnStateKind::BlockStart, Some(0))
14399                .expect("state")
14400                .index(),
14401            1
14402        );
14403        assert_eq!(
14404            atn.add_state(AtnStateKind::Basic, Some(0))
14405                .expect("state")
14406                .index(),
14407            2
14408        );
14409        assert_eq!(
14410            atn.add_state(AtnStateKind::Basic, Some(0))
14411                .expect("state")
14412                .index(),
14413            3
14414        );
14415        assert_eq!(
14416            atn.add_state(AtnStateKind::Basic, Some(0))
14417                .expect("state")
14418                .index(),
14419            4
14420        );
14421        assert_eq!(
14422            atn.add_state(AtnStateKind::RuleStop, Some(0))
14423                .expect("state")
14424                .index(),
14425            5
14426        );
14427        atn.set_rule_to_start_state(vec![0])
14428            .expect("rule start states");
14429        atn.set_rule_to_stop_state(vec![5])
14430            .expect("rule stop states");
14431        atn.add_decision_state(1).expect("decision state");
14432        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14433            .expect("transition");
14434        // Optional block: match A then fall through, or skip straight to state 3.
14435        atn.add_transition(
14436            1,
14437            ParserTransitionSpec::Atom {
14438                target: 3,
14439                label: 1,
14440            },
14441        )
14442        .expect("transition");
14443        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14444            .expect("transition");
14445        // Match B, then EOF.
14446        atn.add_transition(
14447            3,
14448            ParserTransitionSpec::Atom {
14449                target: 4,
14450                label: 2,
14451            },
14452        )
14453        .expect("transition");
14454        atn.add_transition(
14455            4,
14456            ParserTransitionSpec::Atom {
14457                target: 5,
14458                label: TOKEN_EOF,
14459            },
14460        )
14461        .expect("transition");
14462        finish_atn(atn)
14463    }
14464
14465    #[test]
14466    fn sync_decision_deletes_only_a_single_token() {
14467        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
14468        // expected. `(A)? B EOF` at the optional-block decision:
14469        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
14470        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
14471        //               without consuming and records the expected set for the
14472        //               subsequent mismatch (the parser must not over-consume both
14473        //               `C`s and accept the input).
14474        let atn = optional_then_b_eof_atn();
14475
14476        let mut single = mini_parser(vec![
14477            TestToken::new(3).with_text("c"),
14478            TestToken::new(2).with_text("b"),
14479            TestToken::eof("parser-test", 1, 2, 2),
14480        ]);
14481        single.rule_context_stack = vec![RuleContextFrame {
14482            rule_index: 0,
14483            invoking_state: 0,
14484        }];
14485        let children = single
14486            .sync_decision(&atn, 1, true, false)
14487            .expect("single extraneous token recovers");
14488        assert_eq!(children.len(), 1);
14489        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
14490        assert_eq!(single.number_of_syntax_errors(), 1);
14491        // Exactly one token consumed (the cursor now sits on `b`).
14492        assert_eq!(single.la(1), 2);
14493
14494        let mut double = mini_parser(vec![
14495            TestToken::new(3).with_text("c"),
14496            TestToken::new(3).with_text("c"),
14497            TestToken::new(2).with_text("b"),
14498            TestToken::eof("parser-test", 1, 3, 3),
14499        ]);
14500        double.rule_context_stack = vec![RuleContextFrame {
14501            rule_index: 0,
14502            invoking_state: 0,
14503        }];
14504        let result = double.sync_decision(&atn, 1, true, false);
14505        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
14506        // consume either `c`. It reports the mismatch at the first `c` (so the parser
14507        // does not over-consume both and accept the input). Nothing is consumed, so
14508        // the cursor still sits on the first `c` for rule-level recovery.
14509        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
14510        match error {
14511            AntlrError::ParserError { message, .. } => {
14512                assert!(message.starts_with("mismatched input"), "got: {message}");
14513            }
14514            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
14515        }
14516        assert_eq!(double.la(1), 3);
14517    }
14518
14519    /// The real serialized ATN that `antlr4-rust-gen` emits for
14520    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
14521    /// the loop is `EOF`. The loop decision is state 5.
14522    fn star_loop_then_eof_atn() -> Atn {
14523        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14524            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,
14525            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,
14526            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,
14527            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
14528        ]))
14529        .deserialize_parser()
14530        .expect("star-loop-then-EOF ATN should deserialize")
14531    }
14532
14533    /// ATN for `s : a+ Y ; a : X ;`.
14534    ///
14535    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
14536    /// `+` loop must not treat that zero-width child as a successful iteration
14537    /// and then re-enter the loop at the same token index.
14538    fn plus_loop_with_recovering_body_atn() -> Atn {
14539        let mut atn = ParserAtnBuilder::new(2);
14540        assert_eq!(
14541            atn.add_state(AtnStateKind::RuleStart, Some(0))
14542                .expect("state")
14543                .index(),
14544            0
14545        );
14546        assert_eq!(
14547            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
14548                .expect("state")
14549                .index(),
14550            1
14551        );
14552        assert_eq!(
14553            atn.add_state(AtnStateKind::Basic, Some(0))
14554                .expect("state")
14555                .index(),
14556            2
14557        );
14558        assert_eq!(
14559            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14560                .expect("state")
14561                .index(),
14562            3
14563        );
14564        assert_eq!(
14565            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
14566                .expect("state")
14567                .index(),
14568            4
14569        );
14570        assert_eq!(
14571            atn.add_state(AtnStateKind::LoopEnd, Some(0))
14572                .expect("state")
14573                .index(),
14574            5
14575        );
14576        assert_eq!(
14577            atn.add_state(AtnStateKind::RuleStop, Some(0))
14578                .expect("state")
14579                .index(),
14580            6
14581        );
14582        assert_eq!(
14583            atn.add_state(AtnStateKind::RuleStart, Some(1))
14584                .expect("state")
14585                .index(),
14586            7
14587        );
14588        assert_eq!(
14589            atn.add_state(AtnStateKind::Basic, Some(1))
14590                .expect("state")
14591                .index(),
14592            8
14593        );
14594        assert_eq!(
14595            atn.add_state(AtnStateKind::RuleStop, Some(1))
14596                .expect("state")
14597                .index(),
14598            9
14599        );
14600        atn.set_rule_to_start_state(vec![0, 7])
14601            .expect("rule start states");
14602        atn.set_rule_to_stop_state(vec![6, 9])
14603            .expect("rule stop states");
14604        atn.set_end_state(1, 3).expect("block end state");
14605        atn.set_loop_back_state(5, 4).expect("loop back state");
14606        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14607            .expect("transition");
14608        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14609            .expect("transition");
14610        atn.add_transition(
14611            2,
14612            ParserTransitionSpec::Rule {
14613                target: 7,
14614                rule_index: 1,
14615                follow_state: 3,
14616                precedence: 0,
14617            },
14618        )
14619        .expect("transition");
14620        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14621            .expect("transition");
14622        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
14623            .expect("transition");
14624        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14625            .expect("transition");
14626        atn.add_transition(
14627            5,
14628            ParserTransitionSpec::Atom {
14629                target: 6,
14630                label: 2,
14631            },
14632        )
14633        .expect("transition");
14634        atn.add_transition(
14635            7,
14636            ParserTransitionSpec::Atom {
14637                target: 8,
14638                label: 1,
14639            },
14640        )
14641        .expect("transition");
14642        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14643            .expect("transition");
14644        finish_atn(atn)
14645    }
14646
14647    #[test]
14648    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
14649        let atn = plus_loop_with_recovering_body_atn();
14650        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14651
14652        let error = parser
14653            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
14654            .expect_err("EOF recovery should report a bounded mismatch");
14655
14656        let AntlrError::ParserError { message, .. } = error else {
14657            panic!("expected ParserError, got {error:?}");
14658        };
14659        insta::assert_snapshot!(message, @"mismatched input '<EOF>' expecting {'x', 2}");
14660        assert_eq!(parser.number_of_syntax_errors(), 1);
14661        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
14662    }
14663
14664    #[test]
14665    fn sync_decision_deletes_token_before_eof_at_loop_back() {
14666        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
14667        // At the loop ENTRY (loop_back = false) a single unexpected token before
14668        // EOF is deleted as an error node (then the generated EOF match consumes
14669        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
14670        // EOF must be a valid scan-stop for this to fire.
14671        let atn = star_loop_then_eof_atn();
14672        let mut parser = mini_parser(vec![
14673            TestToken::new(2).with_text("c"),
14674            TestToken::eof("parser-test", 1, 1, 1),
14675        ]);
14676        parser.rule_context_stack = vec![RuleContextFrame {
14677            rule_index: 0,
14678            invoking_state: 0,
14679        }];
14680        let children = parser
14681            .sync_decision(&atn, 5, true, false)
14682            .expect("single token before EOF recovers");
14683        assert_eq!(children.len(), 1);
14684        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
14685        assert_eq!(parser.number_of_syntax_errors(), 1);
14686        assert_eq!(
14687            parser.la(1),
14688            TOKEN_EOF,
14689            "EOF is left for the rule's EOF match"
14690        );
14691    }
14692
14693    #[test]
14694    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
14695        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
14696        // single-token deletion, which fails because LA(2) = `c` is not expected —
14697        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
14698        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
14699        let atn = star_loop_then_eof_atn();
14700        let mut parser = mini_parser(vec![
14701            TestToken::new(2).with_text("c"),
14702            TestToken::new(2).with_text("c"),
14703            TestToken::eof("parser-test", 1, 2, 2),
14704        ]);
14705        parser.rule_context_stack = vec![RuleContextFrame {
14706            rule_index: 0,
14707            invoking_state: 0,
14708        }];
14709        let error = parser
14710            .sync_decision(&atn, 5, true, false)
14711            .expect_err("two tokens at the loop entry must not be deleted");
14712        match error {
14713            AntlrError::ParserError { message, .. } => {
14714                assert!(message.starts_with("mismatched input"), "got: {message}");
14715            }
14716            other => panic!("expected mismatched-input ParserError, got {other:?}"),
14717        }
14718        assert_eq!(
14719            parser.la(1),
14720            2,
14721            "nothing consumed; cursor still on first `c`"
14722        );
14723    }
14724
14725    #[test]
14726    fn sync_decision_consumes_until_eof_at_loop_back() {
14727        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
14728        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
14729        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
14730        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
14731        // post-`a` state directly: `c c <EOF>` with loop_back = true.
14732        let atn = star_loop_then_eof_atn();
14733        let mut parser = mini_parser(vec![
14734            TestToken::new(2).with_text("c"),
14735            TestToken::new(2).with_text("c"),
14736            TestToken::eof("parser-test", 1, 2, 2),
14737        ]);
14738        parser.rule_context_stack = vec![RuleContextFrame {
14739            rule_index: 0,
14740            invoking_state: 0,
14741        }];
14742        let children = parser
14743            .sync_decision(&atn, 5, false, true)
14744            .expect("loop-back multi-token deletion recovers onto EOF");
14745        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
14746        assert!(
14747            children
14748                .iter()
14749                .all(|child| parser.node(*child).kind() == NodeKind::Error)
14750        );
14751        assert_eq!(parser.number_of_syntax_errors(), 1);
14752        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
14753    }
14754
14755    fn predicate_after_token_atn() -> Atn {
14756        let mut atn = ParserAtnBuilder::new(2);
14757        assert_eq!(
14758            atn.add_state(AtnStateKind::RuleStart, Some(0))
14759                .expect("state")
14760                .index(),
14761            0
14762        );
14763        assert_eq!(
14764            atn.add_state(AtnStateKind::Basic, Some(0))
14765                .expect("state")
14766                .index(),
14767            1
14768        );
14769        assert_eq!(
14770            atn.add_state(AtnStateKind::Basic, Some(0))
14771                .expect("state")
14772                .index(),
14773            2
14774        );
14775        assert_eq!(
14776            atn.add_state(AtnStateKind::Basic, Some(0))
14777                .expect("state")
14778                .index(),
14779            3
14780        );
14781        assert_eq!(
14782            atn.add_state(AtnStateKind::RuleStop, Some(0))
14783                .expect("state")
14784                .index(),
14785            4
14786        );
14787        atn.set_rule_to_start_state(vec![0])
14788            .expect("rule start states");
14789        atn.set_rule_to_stop_state(vec![4])
14790            .expect("rule stop states");
14791        atn.add_transition(
14792            0,
14793            ParserTransitionSpec::Atom {
14794                target: 1,
14795                label: 1,
14796            },
14797        )
14798        .expect("transition");
14799        atn.add_transition(
14800            1,
14801            ParserTransitionSpec::Predicate {
14802                target: 2,
14803                rule_index: 0,
14804                pred_index: 0,
14805                context_dependent: false,
14806            },
14807        )
14808        .expect("transition");
14809        atn.add_transition(
14810            2,
14811            ParserTransitionSpec::Atom {
14812                target: 3,
14813                label: 2,
14814            },
14815        )
14816        .expect("transition");
14817        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14818            .expect("transition");
14819        finish_atn(atn)
14820    }
14821
14822    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
14823        let mut atn = ParserAtnBuilder::new(1);
14824        for (state_number, kind) in [
14825            (0, AtnStateKind::RuleStart),
14826            (1, AtnStateKind::BlockStart),
14827            (2, AtnStateKind::Basic),
14828            (3, AtnStateKind::Basic),
14829            (4, AtnStateKind::Basic),
14830            (5, AtnStateKind::Basic),
14831            (6, AtnStateKind::BlockEnd),
14832            (7, AtnStateKind::RuleStop),
14833        ] {
14834            assert_eq!(
14835                atn.add_state(kind, Some(0)).expect("state").index(),
14836                state_number
14837            );
14838        }
14839        atn.set_rule_to_start_state(vec![0])
14840            .expect("rule start states");
14841        atn.set_rule_to_stop_state(vec![7])
14842            .expect("rule stop states");
14843        atn.add_decision_state(1).expect("decision state");
14844        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14845            .expect("transition");
14846        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14847            .expect("transition");
14848        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14849            .expect("transition");
14850        atn.add_transition(
14851            2,
14852            ParserTransitionSpec::Predicate {
14853                target: 4,
14854                rule_index: 0,
14855                pred_index: pred_indexes[0],
14856                context_dependent: false,
14857            },
14858        )
14859        .expect("transition");
14860        atn.add_transition(
14861            3,
14862            ParserTransitionSpec::Predicate {
14863                target: 5,
14864                rule_index: 0,
14865                pred_index: pred_indexes[1],
14866                context_dependent: false,
14867            },
14868        )
14869        .expect("transition");
14870        atn.add_transition(
14871            4,
14872            ParserTransitionSpec::Atom {
14873                target: 6,
14874                label: 1,
14875            },
14876        )
14877        .expect("transition");
14878        atn.add_transition(
14879            5,
14880            ParserTransitionSpec::Atom {
14881                target: 6,
14882                label: 1,
14883            },
14884        )
14885        .expect("transition");
14886        atn.add_transition(
14887            6,
14888            ParserTransitionSpec::Atom {
14889                target: 7,
14890                label: TOKEN_EOF,
14891            },
14892        )
14893        .expect("transition");
14894        finish_atn(atn)
14895    }
14896
14897    fn nested_nullable_context_atn() -> Atn {
14898        let mut atn = ParserAtnBuilder::new(1);
14899        for state_number in 0..=20 {
14900            let kind = match state_number {
14901                0 | 10 | 16 => AtnStateKind::RuleStart,
14902                9 | 15 | 20 => AtnStateKind::RuleStop,
14903                _ => AtnStateKind::Basic,
14904            };
14905            let rule_index = match state_number {
14906                0..=9 => 0,
14907                10..=15 => 1,
14908                _ => 2,
14909            };
14910            assert_eq!(
14911                atn.add_state(kind, Some(rule_index))
14912                    .expect("state")
14913                    .index(),
14914                state_number
14915            );
14916        }
14917        atn.set_rule_to_start_state(vec![0, 10, 16])
14918            .expect("rule start states");
14919        atn.set_rule_to_stop_state(vec![9, 15, 20])
14920            .expect("rule stop states");
14921        atn.add_transition(
14922            1,
14923            ParserTransitionSpec::Rule {
14924                target: 10,
14925                rule_index: 1,
14926                follow_state: 8,
14927                precedence: 0,
14928            },
14929        )
14930        .expect("transition");
14931        atn.add_transition(
14932            8,
14933            ParserTransitionSpec::Atom {
14934                target: 9,
14935                label: 1,
14936            },
14937        )
14938        .expect("transition");
14939        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14940            .expect("transition");
14941        atn.add_transition(
14942            2,
14943            ParserTransitionSpec::Rule {
14944                target: 16,
14945                rule_index: 2,
14946                follow_state: 14,
14947                precedence: 0,
14948            },
14949        )
14950        .expect("transition");
14951        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
14952            .expect("transition");
14953        finish_atn(atn)
14954    }
14955
14956    fn generated_match_recovery_atn() -> Atn {
14957        let mut atn = ParserAtnBuilder::new(2);
14958        assert_eq!(
14959            atn.add_state(AtnStateKind::RuleStart, Some(0))
14960                .expect("state")
14961                .index(),
14962            0
14963        );
14964        assert_eq!(
14965            atn.add_state(AtnStateKind::Basic, Some(0))
14966                .expect("state")
14967                .index(),
14968            1
14969        );
14970        assert_eq!(
14971            atn.add_state(AtnStateKind::Basic, Some(0))
14972                .expect("state")
14973                .index(),
14974            2
14975        );
14976        assert_eq!(
14977            atn.add_state(AtnStateKind::RuleStop, Some(0))
14978                .expect("state")
14979                .index(),
14980            3
14981        );
14982        assert_eq!(
14983            atn.add_state(AtnStateKind::RuleStart, Some(1))
14984                .expect("state")
14985                .index(),
14986            4
14987        );
14988        assert_eq!(
14989            atn.add_state(AtnStateKind::RuleStop, Some(1))
14990                .expect("state")
14991                .index(),
14992            5
14993        );
14994        atn.set_rule_to_start_state(vec![0, 4])
14995            .expect("rule start states");
14996        atn.set_rule_to_stop_state(vec![3, 5])
14997            .expect("rule stop states");
14998        atn.add_transition(
14999            1,
15000            ParserTransitionSpec::Rule {
15001                target: 4,
15002                rule_index: 1,
15003                follow_state: 2,
15004                precedence: 0,
15005            },
15006        )
15007        .expect("transition");
15008        atn.add_transition(
15009            2,
15010            ParserTransitionSpec::Atom {
15011                target: 3,
15012                label: TOKEN_EOF,
15013            },
15014        )
15015        .expect("transition");
15016        finish_atn(atn)
15017    }
15018
15019    fn complement_set_atn() -> Atn {
15020        let mut atn = ParserAtnBuilder::new(1);
15021        assert_eq!(
15022            atn.add_state(AtnStateKind::RuleStart, Some(0))
15023                .expect("state")
15024                .index(),
15025            0
15026        );
15027        assert_eq!(
15028            atn.add_state(AtnStateKind::RuleStop, Some(0))
15029                .expect("state")
15030                .index(),
15031            1
15032        );
15033        atn.set_rule_to_start_state(vec![0])
15034            .expect("rule start states");
15035        atn.set_rule_to_stop_state(vec![1])
15036            .expect("rule stop states");
15037        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
15038        atn.add_transition(
15039            0,
15040            ParserTransitionSpec::NotSet {
15041                target: 1,
15042                set: excluded,
15043            },
15044        )
15045        .expect("transition");
15046        finish_atn(atn)
15047    }
15048
15049    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
15050    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
15051    fn wildcard_then_eof_atn() -> Atn {
15052        let mut atn = ParserAtnBuilder::new(1);
15053        assert_eq!(
15054            atn.add_state(AtnStateKind::RuleStart, Some(0))
15055                .expect("state")
15056                .index(),
15057            0
15058        );
15059        assert_eq!(
15060            atn.add_state(AtnStateKind::RuleStop, Some(0))
15061                .expect("state")
15062                .index(),
15063            1
15064        );
15065        assert_eq!(
15066            atn.add_state(AtnStateKind::Basic, Some(0))
15067                .expect("state")
15068                .index(),
15069            2
15070        );
15071        atn.set_rule_to_start_state(vec![0])
15072            .expect("rule start states");
15073        atn.set_rule_to_stop_state(vec![1])
15074            .expect("rule stop states");
15075        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
15076            .expect("transition");
15077        atn.add_transition(
15078            2,
15079            ParserTransitionSpec::Atom {
15080                target: 1,
15081                label: TOKEN_EOF,
15082            },
15083        )
15084        .expect("transition");
15085        finish_atn(atn)
15086    }
15087
15088    #[test]
15089    fn parser_matches_token_and_reports_mismatch() {
15090        let source = Source {
15091            tokens: vec![
15092                TestToken::new(1).with_text("x"),
15093                TestToken::eof("parser-test", 1, 1, 1),
15094            ],
15095            index: 0,
15096        };
15097        let data = RecognizerData::new(
15098            "Mini.g4",
15099            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15100        );
15101        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
15102        let matched = parser.match_token(1).expect("token 1 should match");
15103        assert_eq!(parser.node(matched).text(), "x");
15104        assert!(parser.match_token(1).is_err());
15105    }
15106
15107    #[test]
15108    fn parser_matches_token_sets() {
15109        let mut parser = mini_parser(vec![
15110            TestToken::new(1).with_text("x"),
15111            TestToken::eof("parser-test", 1, 1, 1),
15112        ]);
15113
15114        let matched = parser
15115            .match_set(&[(1, 1), (3, 4)])
15116            .expect("token set should match");
15117        assert_eq!(parser.node(matched).text(), "x");
15118        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
15119    }
15120
15121    #[test]
15122    fn generated_rule_api_tracks_state_and_precedence() {
15123        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15124
15125        let context = parser.enter_rule(7, 2);
15126        assert_eq!(context.rule_index(), 2);
15127        assert_eq!(parser.state(), 7);
15128        assert_eq!(
15129            parser.rule_context_stack,
15130            vec![RuleContextFrame {
15131                rule_index: 2,
15132                invoking_state: 7
15133            }]
15134        );
15135
15136        let recursive = parser.enter_recursion_rule(11, 3, 4);
15137        assert_eq!(recursive.rule_index(), 3);
15138        assert!(parser.precpred(4));
15139        assert!(parser.precpred(5));
15140        assert!(!parser.precpred(3));
15141
15142        let next = parser.push_new_recursion_context(13, 3);
15143        assert_eq!(next.invoking_state(), 13);
15144        parser.unroll_recursion_context();
15145        assert_eq!(parser.precedence_stack, vec![0]);
15146        assert_eq!(
15147            parser.rule_context_stack,
15148            vec![RuleContextFrame {
15149                rule_index: 2,
15150                invoking_state: 7
15151            }]
15152        );
15153
15154        parser.exit_rule();
15155        assert!(parser.rule_context_stack.is_empty());
15156    }
15157
15158    #[test]
15159    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
15160        let mut parser = mini_parser(vec![
15161            TestToken::new(1).with_text("x"),
15162            TestToken::eof("parser-test", 1, 1, 1),
15163        ]);
15164        let matched = parser.match_token(1).expect("token should match");
15165        assert_eq!(parser.node(matched).text(), "x");
15166        parser.record_generated_syntax_error();
15167        parser.set_int_member(7, 11);
15168        parser.set_build_parse_trees(false);
15169        parser.set_report_diagnostic_errors(true);
15170        parser.set_prediction_mode(PredictionMode::Sll);
15171        parser.set_bail_on_error(true);
15172        let _context = parser.enter_recursion_rule(9, 0, 4);
15173        parser.pending_invoking_states.push(5);
15174        parser.unknown_predicate_hits.push((0, 1));
15175        parser.unhandled_action_hits.push((0, 2));
15176
15177        parser.reset();
15178
15179        assert_eq!(parser.input.index(), 0);
15180        assert_eq!(parser.la(1), 1);
15181        assert_eq!(parser.state(), -1);
15182        assert_eq!(parser.number_of_syntax_errors(), 0);
15183        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15184        assert!(parser.rule_context_stack.is_empty());
15185        assert!(parser.pending_invoking_states.is_empty());
15186        assert_eq!(parser.precedence_stack, [0]);
15187        assert!(parser.unknown_predicate_hits.is_empty());
15188        assert!(parser.unhandled_action_hits.is_empty());
15189        assert_eq!(parser.int_member(7), Some(11));
15190        assert!(!parser.build_parse_trees());
15191        assert!(parser.report_diagnostic_errors());
15192        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
15193        assert!(parser.bail_on_error());
15194    }
15195
15196    #[test]
15197    fn set_token_stream_replaces_input_and_resets_parser() {
15198        let mut parser = mini_parser(vec![
15199            TestToken::new(1).with_text("old"),
15200            TestToken::eof("parser-test", 1, 1, 1),
15201        ]);
15202        parser.consume();
15203        parser.record_generated_syntax_error();
15204        let replacement = CommonTokenStream::new(Source {
15205            tokens: vec![
15206                TestToken::new(2).with_text("new"),
15207                TestToken::eof("parser-test", 1, 1, 1),
15208            ],
15209            index: 0,
15210        });
15211
15212        parser.set_token_stream(replacement);
15213
15214        assert_eq!(parser.input.index(), 0);
15215        assert_eq!(parser.la(1), 2);
15216        assert_eq!(parser.input.text_all(), "new");
15217        assert_eq!(parser.number_of_syntax_errors(), 0);
15218    }
15219
15220    #[test]
15221    fn active_invocation_states_exclude_the_root_frame() {
15222        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15223
15224        let _root = parser.enter_rule(0, 0);
15225        assert!(parser.active_invocation_states().is_empty());
15226
15227        let marker = parser.push_invoking_state(6);
15228        let _child = parser.enter_rule(2, 1);
15229        parser.discard_invoking_state(marker);
15230        assert_eq!(parser.active_invocation_states(), [6]);
15231
15232        let marker = parser.push_invoking_state(13);
15233        let _grandchild = parser.enter_rule(4, 2);
15234        parser.discard_invoking_state(marker);
15235        assert_eq!(parser.active_invocation_states(), [13, 6]);
15236
15237        parser.exit_rule();
15238        parser.exit_rule();
15239        parser.exit_rule();
15240    }
15241
15242    #[test]
15243    fn parser_predicates_support_token_adjacency() {
15244        let mut parser = mini_parser(vec![
15245            TestToken::new(1).with_text("=").with_span(0, 0),
15246            TestToken::new(1).with_text(">").with_span(1, 1),
15247            TestToken::eof("parser-test", 2, 1, 2),
15248        ]);
15249        parser.consume();
15250        parser.consume();
15251
15252        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
15253
15254        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15255
15256        let mut parser = mini_parser(vec![
15257            TestToken::new(1).with_text("=").with_span(0, 0),
15258            TestToken::new(1)
15259                .with_text(" ")
15260                .with_channel(HIDDEN_CHANNEL)
15261                .with_span(1, 1),
15262            TestToken::new(1).with_text(">").with_span(2, 2),
15263            TestToken::eof("parser-test", 3, 1, 3),
15264        ]);
15265        parser.consume();
15266        parser.consume();
15267
15268        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15269    }
15270
15271    #[test]
15272    fn parser_predicates_support_context_child_text_checks() {
15273        let mut parser = mini_parser(vec![
15274            TestToken::new(1).with_text("var"),
15275            TestToken::eof("parser-test", 1, 1, 1),
15276        ]);
15277        let mut context = ParserRuleContext::new(1, 0);
15278        let mut child_context = ParserRuleContext::new(2, 0);
15279        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
15280        parser.tree.add_child(&mut child_context, terminal);
15281        let child = parser.rule_node(child_context);
15282        parser.tree.add_child(&mut context, child);
15283        let predicates = [(
15284            1,
15285            0,
15286            ParserPredicate::ContextChildRuleTextNotEquals {
15287                rule_index: 2,
15288                text: "var",
15289            },
15290        )];
15291
15292        assert!(
15293            !parser.parser_semantic_predicate_matches_with_context_and_local(
15294                &predicates,
15295                1,
15296                0,
15297                &context,
15298                0,
15299            )
15300        );
15301    }
15302
15303    #[test]
15304    fn context_expected_symbols_walks_nullable_parent_contexts() {
15305        let atn = nested_nullable_context_atn();
15306        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15307        parser.rule_context_stack = vec![
15308            RuleContextFrame {
15309                rule_index: 0,
15310                invoking_state: 0,
15311            },
15312            RuleContextFrame {
15313                rule_index: 1,
15314                invoking_state: 1,
15315            },
15316            RuleContextFrame {
15317                rule_index: 2,
15318                invoking_state: 2,
15319            },
15320        ];
15321
15322        let expected = parser.context_expected_symbols(&atn);
15323
15324        assert!(expected.contains(&1));
15325        assert!(expected.contains(&TOKEN_EOF));
15326    }
15327
15328    #[test]
15329    fn prediction_context_return_states_track_rule_stack_changes() {
15330        let atn = nested_nullable_context_atn();
15331        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15332        parser.rule_context_stack = vec![
15333            RuleContextFrame {
15334                rule_index: 0,
15335                invoking_state: 0,
15336            },
15337            RuleContextFrame {
15338                rule_index: 1,
15339                invoking_state: 1,
15340            },
15341            RuleContextFrame {
15342                rule_index: 2,
15343                invoking_state: 2,
15344            },
15345        ];
15346
15347        let initial_version = parser.rule_context_version();
15348        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15349        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15350        assert_eq!(first, second);
15351        assert_eq!(parser.rule_context_version(), initial_version);
15352
15353        parser.exit_rule();
15354        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15355        assert_ne!(first, after_pop);
15356        assert_ne!(parser.rule_context_version(), initial_version);
15357    }
15358
15359    #[test]
15360    fn generated_match_token_recovers_missing_token_from_context_follow() {
15361        let atn = generated_match_recovery_atn();
15362        let data = RecognizerData::new(
15363            "Mini.g4",
15364            Vocabulary::new(
15365                [None, Some("'X'"), Some("'Y'")],
15366                [None, Some("X"), Some("Y")],
15367                [None::<&str>, None, None],
15368            ),
15369        );
15370        let mut parser = BaseParser::new(
15371            CommonTokenStream::new(Source {
15372                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15373                index: 0,
15374            }),
15375            data,
15376        );
15377        parser.rule_context_stack = vec![
15378            RuleContextFrame {
15379                rule_index: 0,
15380                invoking_state: 0,
15381            },
15382            RuleContextFrame {
15383                rule_index: 1,
15384                invoking_state: 1,
15385            },
15386        ];
15387        assert_eq!(parser.number_of_syntax_errors(), 0);
15388
15389        let node = parser
15390            .match_token_recovering(2, 5, &atn)
15391            .expect("generated match should insert missing token");
15392
15393        assert_eq!(node.children().len(), 1);
15394        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
15395        assert_eq!(
15396            node.clone()
15397                .into_child_iter()
15398                .map(|child| parser.node(child).text())
15399                .collect::<Vec<_>>(),
15400            ["<missing 'Y'>"]
15401        );
15402        // Single-token insertion synthesizes a missing token and consumes nothing,
15403        // so no EOF terminal is consumed even though lookahead is EOF.
15404        assert!(!node.consumed_eof());
15405        assert_eq!(parser.la(1), TOKEN_EOF);
15406        assert_eq!(parser.number_of_syntax_errors(), 1);
15407        assert_eq!(
15408            parser.generated_parser_diagnostics,
15409            [ParserDiagnostic {
15410                line: 1,
15411                column: 3,
15412                message: "missing 'Y' at '<EOF>'".to_owned(),
15413            }]
15414        );
15415    }
15416
15417    #[test]
15418    fn generated_match_token_counts_single_token_deletion_recovery() {
15419        let atn = generated_match_recovery_atn();
15420        let data = RecognizerData::new(
15421            "Mini.g4",
15422            Vocabulary::new(
15423                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15424                [None, Some("X"), Some("Y"), Some("Z")],
15425                [None::<&str>, None, None, None],
15426            ),
15427        );
15428        let mut parser = BaseParser::new(
15429            CommonTokenStream::new(Source {
15430                tokens: vec![
15431                    TestToken::new(3).with_text("z"),
15432                    TestToken::new(2).with_text("y"),
15433                    TestToken::eof("parser-test", 3, 1, 3),
15434                ],
15435                index: 0,
15436            }),
15437            data,
15438        );
15439
15440        let node = parser
15441            .match_token_recovering(2, 5, &atn)
15442            .expect("generated match should delete the extraneous token");
15443
15444        assert_eq!(node.children().len(), 2);
15445        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
15446        assert_eq!(parser.node(node.children()[0]).text(), "z");
15447        assert_eq!(parser.node(node.children()[1]).text(), "y");
15448        assert_eq!(
15449            node.into_child_iter()
15450                .map(|child| parser.node(child).text())
15451                .collect::<Vec<_>>(),
15452            ["z", "y"]
15453        );
15454        assert_eq!(parser.number_of_syntax_errors(), 1);
15455    }
15456
15457    #[test]
15458    fn generated_match_token_iterates_single_success_without_a_children_vec() {
15459        let atn = generated_match_recovery_atn();
15460        let data = RecognizerData::new(
15461            "Mini.g4",
15462            Vocabulary::new(
15463                [None, Some("'X'"), Some("'Y'")],
15464                [None, Some("X"), Some("Y")],
15465                [None::<&str>, None, None],
15466            ),
15467        );
15468        let mut parser = BaseParser::new(
15469            CommonTokenStream::new(Source {
15470                tokens: vec![
15471                    TestToken::new(2).with_text("y"),
15472                    TestToken::eof("parser-test", 1, 1, 1),
15473                ],
15474                index: 0,
15475            }),
15476            data,
15477        );
15478
15479        let node = parser
15480            .match_token_recovering(2, 5, &atn)
15481            .expect("generated match should consume the expected token");
15482
15483        assert_eq!(
15484            node.into_child_iter()
15485                .map(|child| parser.node(child).text())
15486                .collect::<Vec<_>>(),
15487            ["y"]
15488        );
15489        assert_eq!(parser.number_of_syntax_errors(), 0);
15490    }
15491
15492    #[test]
15493    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
15494        let atn = generated_match_recovery_atn();
15495        let data = RecognizerData::new(
15496            "Mini.g4",
15497            Vocabulary::new(
15498                [None, Some("'X'"), Some("'Y'")],
15499                [None, Some("X"), Some("Y")],
15500                [None::<&str>, None, None],
15501            ),
15502        );
15503        let mut parser = BaseParser::new(
15504            CommonTokenStream::new(Source {
15505                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15506                index: 0,
15507            }),
15508            data,
15509        );
15510        parser.rule_context_stack = vec![
15511            RuleContextFrame {
15512                rule_index: 0,
15513                invoking_state: 0,
15514            },
15515            RuleContextFrame {
15516                rule_index: 1,
15517                invoking_state: 1,
15518            },
15519        ];
15520        let marker = parser.generated_diagnostics_checkpoint();
15521
15522        let _ = parser
15523            .match_token_recovering(2, 5, &atn)
15524            .expect("generated match should insert missing token");
15525        assert_eq!(parser.number_of_syntax_errors(), 1);
15526
15527        parser.restore_generated_diagnostics(marker);
15528
15529        assert_eq!(parser.number_of_syntax_errors(), 0);
15530        assert!(parser.generated_parser_diagnostics.is_empty());
15531    }
15532
15533    #[test]
15534    fn generated_prediction_diagnostics_use_adaptive_context() {
15535        let atn = two_alt_decision_atn();
15536        let data = RecognizerData::new(
15537            "Mini.g4",
15538            Vocabulary::new(
15539                [None, Some("'x'"), Some("'y'")],
15540                [None, Some("X"), Some("Y")],
15541                [None::<&str>, None, None],
15542            ),
15543        )
15544        .with_rule_names(["s"]);
15545        let mut parser = BaseParser::new(
15546            CommonTokenStream::new(Source {
15547                tokens: vec![
15548                    TestToken::new(1)
15549                        .with_text("x")
15550                        .with_position(1, 0)
15551                        .with_span(0, 0),
15552                    TestToken::new(2)
15553                        .with_text("y")
15554                        .with_position(1, 2)
15555                        .with_span(1, 1),
15556                    TestToken::eof("parser-test", 2, 1, 3),
15557                ],
15558                index: 0,
15559            }),
15560            data,
15561        );
15562        parser.set_report_diagnostic_errors(true);
15563
15564        parser.record_generated_prediction_diagnostic(
15565            &atn,
15566            1,
15567            &ParserAtnPrediction {
15568                alt: 1,
15569                requires_full_context: true,
15570                has_semantic_context: false,
15571                diagnostic: Some(ParserAtnPredictionDiagnostic {
15572                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
15573                    start_index: 0,
15574                    sll_stop_index: 1,
15575                    ll_stop_index: 0,
15576                    conflicting_alts: vec![1, 2],
15577                    exact: false,
15578                }),
15579            },
15580        );
15581        // Ambiguities from the default LL prediction mode are non-exact, so —
15582        // matching Java's exactOnly DiagnosticErrorListener — only the
15583        // attempting-full-context line is reported. Exact-ambiguity mode
15584        // reports the ambiguity itself.
15585        parser.record_generated_prediction_diagnostic(
15586            &atn,
15587            1,
15588            &ParserAtnPrediction {
15589                alt: 1,
15590                requires_full_context: true,
15591                has_semantic_context: false,
15592                diagnostic: Some(ParserAtnPredictionDiagnostic {
15593                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
15594                    start_index: 0,
15595                    sll_stop_index: 1,
15596                    ll_stop_index: 1,
15597                    conflicting_alts: vec![1, 2],
15598                    exact: false,
15599                }),
15600            },
15601        );
15602
15603        // The full-context/context-sensitivity diagnostic trace (order + decision + input windows)
15604        // is one snapshot rather than three ParserDiagnostic literals.
15605        insta::assert_debug_snapshot!(
15606            "generated_prediction_diagnostics_use_adaptive_context",
15607            parser.generated_parser_diagnostics
15608        );
15609    }
15610
15611    #[test]
15612    fn generated_match_not_set_recovers_empty_complement_at_eof() {
15613        let atn = complement_set_atn();
15614        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15615        parser.rule_context_stack = vec![RuleContextFrame {
15616            rule_index: 0,
15617            invoking_state: 0,
15618        }];
15619
15620        let node = parser
15621            .match_not_token_set_recovering(
15622                atn.token_set(0).expect("excluded token set"),
15623                1,
15624                1,
15625                1,
15626                &atn,
15627            )
15628            .expect("empty complement should recover at EOF");
15629
15630        assert_eq!(node.children().len(), 1);
15631        // Recovery synthesizes a missing token without consuming EOF, so the
15632        // enclosing rule must not record EOF as its stop token.
15633        assert!(!node.consumed_eof());
15634        assert_eq!(parser.la(1), TOKEN_EOF);
15635        assert_eq!(
15636            parser.generated_parser_diagnostics,
15637            [ParserDiagnostic {
15638                line: 1,
15639                column: 1,
15640                message: "missing {} at '<EOF>'".to_owned(),
15641            }]
15642        );
15643    }
15644
15645    #[test]
15646    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
15647        // `start : . EOF ;` on empty input. The wildcard is modeled as an
15648        // empty-complement not-set; at EOF the follow state (the explicit EOF
15649        // match) expects EOF, so even in the start rule recovery must perform
15650        // single-token insertion (`<missing ...>`) rather than aborting — matching
15651        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
15652        let atn = wildcard_then_eof_atn();
15653        let data = RecognizerData::new(
15654            "Mini.g4",
15655            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15656        );
15657        let mut parser = BaseParser::new(
15658            CommonTokenStream::new(Source {
15659                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
15660                index: 0,
15661            }),
15662            data,
15663        );
15664        parser.rule_context_stack = vec![RuleContextFrame {
15665            rule_index: 0,
15666            invoking_state: 0,
15667        }];
15668
15669        let node = parser
15670            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
15671            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
15672
15673        // A single `<missing ...>` error node is inserted; EOF is not consumed.
15674        assert_eq!(node.children().len(), 1);
15675        assert!(!node.consumed_eof());
15676        assert!(
15677            parser
15678                .node(node.children()[0])
15679                .text()
15680                .starts_with("<missing")
15681        );
15682        assert_eq!(parser.la(1), TOKEN_EOF);
15683        assert_eq!(
15684            parser.generated_parser_diagnostics,
15685            [ParserDiagnostic {
15686                line: 1,
15687                column: 1,
15688                message: "missing 'x' at '<EOF>'".to_owned(),
15689            }]
15690        );
15691    }
15692
15693    #[test]
15694    fn generated_rule_recovery_consumes_to_parent_follow() {
15695        let atn = generated_match_recovery_atn();
15696        let data = RecognizerData::new(
15697            "Mini.g4",
15698            Vocabulary::new(
15699                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15700                [None, Some("X"), Some("Y"), Some("Z")],
15701                [None::<&str>, None, None, None],
15702            ),
15703        );
15704        let mut parser = BaseParser::new(
15705            CommonTokenStream::new(Source {
15706                tokens: vec![
15707                    TestToken::new(3).with_text("z"),
15708                    TestToken::eof("parser-test", 1, 1, 1),
15709                ],
15710                index: 0,
15711            }),
15712            data,
15713        );
15714        let _parent = parser.enter_rule(0, 0);
15715        let marker = parser.push_invoking_state(1);
15716        let mut child = parser.enter_rule(4, 1);
15717        parser.discard_invoking_state(marker);
15718
15719        parser.recover_generated_rule(
15720            &mut child,
15721            &atn,
15722            AntlrError::ParserError {
15723                line: 1,
15724                column: 0,
15725                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15726            },
15727        );
15728        let tree = parser.finish_rule(child, false);
15729
15730        assert_eq!(parser.la(1), TOKEN_EOF);
15731        assert_eq!(
15732            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
15733            "(a z)"
15734        );
15735        assert_eq!(parser.number_of_syntax_errors(), 1);
15736        assert_eq!(
15737            parser.generated_parser_diagnostics,
15738            [ParserDiagnostic {
15739                line: 1,
15740                column: 0,
15741                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15742            }]
15743        );
15744        parser.exit_rule();
15745    }
15746
15747    #[test]
15748    fn generated_rule_recovery_forces_progress_after_repeated_error_state() {
15749        let atn = nested_nullable_context_atn();
15750        let mut parser = mini_parser(vec![
15751            TestToken::new(1).with_text("x"),
15752            TestToken::eof("parser-test", 1, 1, 1),
15753        ]);
15754        parser.rule_context_stack = vec![
15755            RuleContextFrame {
15756                rule_index: 0,
15757                invoking_state: 0,
15758            },
15759            RuleContextFrame {
15760                rule_index: 1,
15761                invoking_state: 1,
15762            },
15763            RuleContextFrame {
15764                rule_index: 2,
15765                invoking_state: 2,
15766            },
15767        ];
15768        parser.set_state(20);
15769        let mut context = ParserRuleContext::new(2, 2);
15770
15771        parser.recover_generated_rule(
15772            &mut context,
15773            &atn,
15774            AntlrError::NoViableAlternative {
15775                input: "'x'".to_owned(),
15776            },
15777        );
15778        assert_eq!(parser.input.index(), 0);
15779
15780        parser.set_state(21);
15781        parser.recover_generated_rule(
15782            &mut context,
15783            &atn,
15784            AntlrError::NoViableAlternative {
15785                input: "'x'".to_owned(),
15786            },
15787        );
15788        assert_eq!(parser.input.index(), 0);
15789        assert_eq!(
15790            parser.generated_recovery_error_states,
15791            BTreeSet::from([20, 21])
15792        );
15793
15794        parser.set_state(20);
15795        parser.recover_generated_rule(
15796            &mut context,
15797            &atn,
15798            AntlrError::NoViableAlternative {
15799                input: "'x'".to_owned(),
15800            },
15801        );
15802
15803        assert_eq!(parser.input.index(), 1);
15804        assert_eq!(parser.la(1), TOKEN_EOF);
15805        assert!(context.has_matched_child());
15806        assert_eq!(parser.generated_recovery_error_states, BTreeSet::from([20]));
15807
15808        parser.match_eof().expect("EOF should match");
15809        assert_eq!(parser.generated_recovery_error_index, None);
15810        assert!(parser.generated_recovery_error_states.is_empty());
15811    }
15812
15813    #[test]
15814    fn greedy_ll1_alt_handles_nullable_loop_exit() {
15815        let mut body_symbols = TokenBitSet::default();
15816        body_symbols.insert(1);
15817        let entry = DecisionLookahead {
15818            transitions: vec![
15819                TransitionLookSet {
15820                    symbols: body_symbols,
15821                    nullable: false,
15822                },
15823                TransitionLookSet {
15824                    symbols: TokenBitSet::default(),
15825                    nullable: true,
15826                },
15827            ],
15828        };
15829
15830        assert_eq!(ll1_unique_alt(&entry, 2), None);
15831        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
15832        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
15833        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
15834    }
15835
15836    #[test]
15837    fn ordinary_repetition_builds_tree_in_input_order() {
15838        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15839            let mut parser = mini_parser(repeated_x_tokens(3));
15840            let tree = parser
15841                .parse_atn_rule(&atn, 0)
15842                .expect("ordinary repetition should parse");
15843
15844            let root = parser
15845                .node(tree)
15846                .as_rule()
15847                .expect("entry result should be a rule");
15848            let body_rules = root.child_rules(1).collect::<Vec<_>>();
15849            assert_eq!(root.text(), "xxx<EOF>");
15850            assert_eq!(body_rules.len(), 3);
15851            assert_eq!(
15852                body_rules
15853                    .iter()
15854                    .map(|rule| rule.start_id().expect("body start").index())
15855                    .collect::<Vec<_>>(),
15856                [0, 1, 2]
15857            );
15858            assert_eq!(
15859                body_rules
15860                    .iter()
15861                    .map(|rule| rule.stop_id().expect("body stop").index())
15862                    .collect::<Vec<_>>(),
15863                [0, 1, 2]
15864            );
15865            assert_eq!(parser.number_of_syntax_errors(), 0);
15866        }
15867    }
15868
15869    #[test]
15870    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
15871        const DEPTH: usize = 20_000;
15872
15873        std::thread::Builder::new()
15874            .name("deferred-rule-materialization".to_owned())
15875            .stack_size(256 * 1024)
15876            .spawn(|| {
15877                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15878                let mut root = FastDeferredNodeId::EMPTY;
15879                for depth in 0..DEPTH {
15880                    root = parser
15881                        .recognition_arena
15882                        .deferred_rule_node(FastDeferredRule {
15883                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
15884                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
15885                            start_index: 0,
15886                            stop_index: None,
15887                            deferred_children: root,
15888                            children: NodeSeqId::EMPTY,
15889                        });
15890                }
15891
15892                let (mut children, alt_number) =
15893                    parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
15894                assert_eq!(alt_number, 0);
15895                for expected_rule in (0..DEPTH).rev() {
15896                    let mut nodes = parser.recognition_arena.iter(children);
15897                    let node = nodes.next().expect("nested rule node");
15898                    assert!(nodes.next().is_none(), "each rule has one child");
15899                    let ArenaRecognizedNode::Rule {
15900                        rule_index,
15901                        children: nested,
15902                        ..
15903                    } = parser.recognition_arena.node(node)
15904                    else {
15905                        panic!("expected nested rule");
15906                    };
15907                    assert_eq!(rule_index as usize, expected_rule);
15908                    children = nested;
15909                }
15910                assert!(children.is_empty());
15911            })
15912            .expect("small-stack thread should start")
15913            .join()
15914            .expect("deferred rules should materialize without recursion");
15915    }
15916
15917    #[test]
15918    fn deferred_alternatives_preserve_left_recursive_contexts() {
15919        let mut parser = mini_parser(vec![
15920            TestToken::new(1).with_text("1"),
15921            TestToken::new(2).with_text("+"),
15922            TestToken::new(1).with_text("2"),
15923            TestToken::eof("parser-test", 3, 1, 3),
15924        ]);
15925        let base = parser.arena_token_node(0, false);
15926        let operator = parser.arena_token_node(1, false);
15927        let right = parser.arena_token_node(2, false);
15928
15929        let base = parser.recognition_arena.prepend(NodeSeqId::EMPTY, base);
15930        let base = parser.recognition_arena.deferred_fragment(base);
15931        let operator = parser.recognition_arena.prepend(NodeSeqId::EMPTY, operator);
15932        let operator = parser.recognition_arena.deferred_fragment(operator);
15933        let right = parser.recognition_arena.prepend(NodeSeqId::EMPTY, right);
15934        let right = parser.recognition_arena.deferred_fragment(right);
15935        let base_alt = parser.recognition_arena.deferred_alternative(1);
15936        let boundary = parser.recognition_arena.deferred_left_recursive_boundary(0);
15937        let operator_alt = parser.recognition_arena.deferred_alternative(6);
15938
15939        let mut deferred = FastDeferredNodeId::EMPTY;
15940        for fragment in [base_alt, base, boundary, operator_alt, operator, right] {
15941            deferred = parser
15942                .recognition_arena
15943                .concat_deferred_nodes(deferred, fragment);
15944        }
15945        let (nodes, root_alt_number) =
15946            parser.materialize_fast_deferred_nodes(deferred, NodeSeqId::EMPTY);
15947        let nodes = parser
15948            .recognition_arena
15949            .fold_left_recursive_boundaries(nodes);
15950
15951        let mut root = ParserRuleContext::new(0, -1);
15952        root.set_context_alt_number(root_alt_number);
15953        let mut cursor = nodes;
15954        while let Some(link) = parser.recognition_arena.link(cursor) {
15955            let child = parser
15956                .arena_recognized_node_tree(link.head, false, true)
15957                .expect("materialized child should become a public tree");
15958            parser.tree.add_child(&mut root, child);
15959            cursor = link.tail;
15960        }
15961        let tree = parser.rule_node(root);
15962        let contexts = parser
15963            .node(tree)
15964            .descendants()
15965            .filter_map(Node::as_rule)
15966            .map(|rule| {
15967                (
15968                    rule.rule_index(),
15969                    rule.alt_number(),
15970                    rule.context_alt_number(),
15971                    rule.text(),
15972                )
15973            })
15974            .collect::<Vec<_>>();
15975
15976        insta::assert_debug_snapshot!(
15977            "deferred_alternatives_preserve_left_recursive_contexts",
15978            contexts
15979        );
15980    }
15981
15982    #[test]
15983    fn fast_recognizer_preserves_labeled_left_recursive_operator_context() {
15984        let atn = labeled_left_recursive_operator_atn();
15985        let mut parser = mini_parser(vec![
15986            TestToken::new(1).with_text("a"),
15987            TestToken::new(3).with_text("+"),
15988            TestToken::new(1).with_text("b"),
15989            TestToken::eof("parser-test", 3, 1, 3),
15990        ]);
15991
15992        let (tree, _) = parser
15993            .parse_atn_rule_with_runtime_options(
15994                &atn,
15995                0,
15996                ParserRuntimeOptions {
15997                    track_context_alt_numbers: true,
15998                    ..ParserRuntimeOptions::default()
15999                },
16000            )
16001            .expect("labeled left-recursive addition should parse");
16002        let contexts = parser
16003            .node(tree)
16004            .descendants()
16005            .filter_map(Node::as_rule)
16006            .map(|rule| {
16007                let operator = rule
16008                    .children()
16009                    .next()
16010                    .and_then(Node::as_rule)
16011                    .is_some_and(|child| child.rule_index() == rule.rule_index());
16012                (operator, rule.context_alt_number(), rule.text())
16013            })
16014            .collect::<Vec<_>>();
16015
16016        insta::assert_debug_snapshot!(
16017            "fast_recognizer_preserves_labeled_left_recursive_operator_context",
16018            contexts
16019        );
16020        assert!(!parser.recognition_arena.deferred_nodes.is_empty());
16021        assert_eq!(parser.number_of_syntax_errors(), 0);
16022    }
16023
16024    #[test]
16025    fn deeply_nested_rule_calls_grow_the_stack() {
16026        const DEPTH: usize = 4_096;
16027        const STACK_SIZE: usize = 256 * 1024;
16028        let atn = nested_rule_chain_atn(DEPTH);
16029        std::thread::Builder::new()
16030            .name("nested-adaptive-set-rules".to_owned())
16031            .stack_size(STACK_SIZE)
16032            .spawn(move || {
16033                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16034                parser.set_build_parse_trees(false);
16035                // This test isolates recognizer depth from the separately
16036                // cached FIRST-set metadata walk.
16037                parser.fast_first_set_prefilter = false;
16038                parser
16039                    .parse_atn_rule(&atn, 0)
16040                    .expect("nested rule chain should grow the native stack");
16041                assert_eq!(parser.input.index(), 1);
16042            })
16043            .expect("small-stack thread should start")
16044            .join()
16045            .expect("nested rule chain should not overflow its stack");
16046    }
16047
16048    #[test]
16049    fn deeply_nested_branching_rules_grow_the_stack() {
16050        const DEPTH: usize = 4_096;
16051        const STACK_SIZE: usize = 256 * 1024;
16052        let atn = nested_rule_graph_atn(DEPTH, true, false);
16053        std::thread::Builder::new()
16054            .name("nested-branching-rules".to_owned())
16055            .stack_size(STACK_SIZE)
16056            .spawn(move || {
16057                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16058                parser.set_build_parse_trees(false);
16059                parser
16060                    .parse_atn_rule(&atn, 0)
16061                    .expect("branching rule chain should grow the native stack");
16062                assert_eq!(parser.input.index(), 1);
16063            })
16064            .expect("small-stack thread should start")
16065            .join()
16066            .expect("branching rule chain should not overflow its stack");
16067    }
16068
16069    #[test]
16070    fn deeply_nested_rule_follows_grow_the_stack() {
16071        const DEPTH: usize = 4_096;
16072        const STACK_SIZE: usize = 256 * 1024;
16073        let atn = nested_rule_graph_atn(DEPTH, false, true);
16074        std::thread::Builder::new()
16075            .name("nested-rule-follows".to_owned())
16076            .stack_size(STACK_SIZE)
16077            .spawn(move || {
16078                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
16079                parser.set_build_parse_trees(false);
16080                parser.fast_first_set_prefilter = false;
16081                parser
16082                    .parse_atn_rule(&atn, 0)
16083                    .expect("rule follow chain should grow the native stack");
16084                assert_eq!(parser.input.index(), DEPTH);
16085            })
16086            .expect("small-stack thread should start")
16087            .join()
16088            .expect("nested rule follow chain should not overflow its stack");
16089    }
16090
16091    #[test]
16092    fn deeply_nested_recovery_grows_the_stack() {
16093        const DEPTH: usize = 4_096;
16094        const STACK_SIZE: usize = 256 * 1024;
16095        let atn = nested_rule_chain_atn(DEPTH);
16096        std::thread::Builder::new()
16097            .name("nested-rule-recovery".to_owned())
16098            .stack_size(STACK_SIZE)
16099            .spawn(move || {
16100                let mut parser = mini_parser(vec![
16101                    TestToken::new(2).with_text("z"),
16102                    TestToken::new(1).with_text("x"),
16103                    TestToken::eof("parser-test", 2, 1, 2),
16104                ]);
16105                parser.set_build_parse_trees(false);
16106                parser.fast_first_set_prefilter = false;
16107                parser
16108                    .parse_atn_rule(&atn, 0)
16109                    .expect("nested recovery should grow the native stack");
16110                assert_eq!(parser.input.index(), 2);
16111                assert_eq!(parser.number_of_syntax_errors(), 1);
16112            })
16113            .expect("small-stack thread should start")
16114            .join()
16115            .expect("nested rule recovery should not overflow its stack");
16116    }
16117
16118    #[test]
16119    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
16120        const REPETITIONS: usize = 64;
16121
16122        let atn = ambiguous_ordinary_star_loop_atn();
16123        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16124        let tree = parser
16125            .parse_atn_rule(&atn, 0)
16126            .expect("ambiguous ordinary repetition should parse");
16127
16128        let root = parser
16129            .node(tree)
16130            .as_rule()
16131            .expect("entry result should be a rule");
16132        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
16133        assert_eq!(parser.input.index(), REPETITIONS);
16134        assert!(
16135            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
16136            "equivalent segmentations should keep deferred storage linear"
16137        );
16138        assert_eq!(parser.number_of_syntax_errors(), 0);
16139    }
16140
16141    #[test]
16142    fn long_ordinary_repetition_does_not_consume_native_stack() {
16143        const REPETITIONS: usize = 20_000;
16144
16145        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16146            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16147            parser.set_build_parse_trees(false);
16148            parser
16149                .parse_atn_rule(&atn, 0)
16150                .expect("long ordinary repetition should parse");
16151
16152            assert_eq!(parser.input.index(), REPETITIONS);
16153            assert_eq!(parser.number_of_syntax_errors(), 0);
16154        }
16155    }
16156
16157    #[test]
16158    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
16159        const REPETITIONS: usize = 2_000;
16160        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
16161
16162        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16163            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16164            let tree = parser
16165                .parse_atn_rule(&atn, 0)
16166                .expect("long rule repetition should parse");
16167
16168            let root = parser
16169                .node(tree)
16170                .as_rule()
16171                .expect("entry result should be a rule");
16172            assert_eq!(root.text(), expected_text);
16173            assert_eq!(root.child_rules(1).count(), REPETITIONS);
16174            let first_body = root.child_rules(1).next().expect("first body rule");
16175            let last_body = root.child_rules(1).next_back().expect("last body rule");
16176            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
16177            assert_eq!(
16178                last_body.stop_id().expect("last body stop").index(),
16179                REPETITIONS - 1
16180            );
16181
16182            let stats = parser.recognition_arena_stats();
16183            assert_eq!(
16184                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16185                (REPETITIONS, REPETITIONS, 0)
16186            );
16187            assert_eq!(
16188                (stats.total_links, stats.live_links, stats.dead_links),
16189                (REPETITIONS, REPETITIONS, 0)
16190            );
16191            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
16192            assert_eq!(
16193                parser.recognition_arena.deferred_nodes.len(),
16194                REPETITIONS * 2 - 1
16195            );
16196            assert_eq!(parser.number_of_syntax_errors(), 0);
16197        }
16198    }
16199
16200    #[test]
16201    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
16202        let key = |state_number| FastRecognizeKey {
16203            state_number,
16204            stop_state: 10,
16205            index: state_number,
16206            rule_start_index: 0,
16207            decision_start_index: None,
16208            precedence: 0,
16209            recovery_symbols_id: 0,
16210            recovery_state: None,
16211        };
16212
16213        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16214        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
16215            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
16216        }
16217        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
16218        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
16219
16220        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16221        let repeated = key(1);
16222        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
16223            assert!(promote.clean_memo_enabled_for_key(&repeated));
16224        }
16225        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
16226
16227        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
16228            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
16229        }
16230        assert!(sparse.clean_memo_enabled_for_key(&repeated));
16231        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
16232        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
16233            assert!(sparse.clean_memo_enabled_for_key(&repeated));
16234        }
16235        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
16236    }
16237
16238    #[test]
16239    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
16240        assert_eq!(
16241            fast_recognize_memo_capacity(0),
16242            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16243        );
16244        assert_eq!(
16245            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
16246            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16247        );
16248        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
16249        assert_eq!(
16250            fast_recognize_memo_capacity(usize::MAX),
16251            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
16252        );
16253    }
16254
16255    #[test]
16256    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
16257        let mut scratch = FastRecognizeTopScratch::default();
16258        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16259        let retained_capacity = scratch.memo.capacity();
16260        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16261        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16262
16263        let larger_capacity = retained_capacity + 1;
16264        scratch.prepare(larger_capacity);
16265        let grown_capacity = scratch.memo.capacity();
16266        assert!(grown_capacity >= larger_capacity);
16267        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16268
16269        scratch.memo.insert(
16270            FastRecognizeKey {
16271                state_number: 0,
16272                stop_state: 0,
16273                index: 0,
16274                rule_start_index: 0,
16275                decision_start_index: None,
16276                precedence: 0,
16277                recovery_symbols_id: 0,
16278                recovery_state: None,
16279            },
16280            Rc::from([FastRecognizeOutcome {
16281                index: 0,
16282                consumed_eof: false,
16283                diagnostics: DiagnosticSeqId::EMPTY,
16284                deferred_nodes: FastDeferredNodeId::EMPTY,
16285                nodes: NodeSeqId::EMPTY,
16286            }]),
16287        );
16288        scratch.release_oversized_memo();
16289        assert!(scratch.memo.is_empty());
16290        assert_eq!(scratch.memo.capacity(), grown_capacity);
16291
16292        scratch
16293            .memo
16294            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
16295        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16296
16297        scratch.release_oversized_memo();
16298        assert!(scratch.memo.is_empty());
16299        assert_eq!(scratch.memo.capacity(), 0);
16300    }
16301
16302    #[test]
16303    fn clean_empty_multi_alt_outcomes_are_memoized() {
16304        let mut atn = ParserAtnBuilder::new(2);
16305        assert_eq!(
16306            atn.add_state(AtnStateKind::RuleStart, Some(0))
16307                .expect("state")
16308                .index(),
16309            0
16310        );
16311        assert_eq!(
16312            atn.add_state(AtnStateKind::BlockStart, Some(0))
16313                .expect("state")
16314                .index(),
16315            1
16316        );
16317        assert_eq!(
16318            atn.add_state(AtnStateKind::RuleStop, Some(0))
16319                .expect("state")
16320                .index(),
16321            2
16322        );
16323        atn.set_rule_to_start_state(vec![0])
16324            .expect("rule start states");
16325        atn.set_rule_to_stop_state(vec![2])
16326            .expect("rule stop states");
16327        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16328            .expect("transition");
16329        atn.add_transition(
16330            1,
16331            ParserTransitionSpec::Atom {
16332                target: 2,
16333                label: 1,
16334            },
16335        )
16336        .expect("transition");
16337        atn.add_transition(
16338            1,
16339            ParserTransitionSpec::Atom {
16340                target: 2,
16341                label: 2,
16342            },
16343        )
16344        .expect("transition");
16345        let atn = finish_atn(atn);
16346
16347        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16348        parser.fast_recovery_enabled = false;
16349        let mut visiting = FxHashSet::default();
16350        let mut memo = FxHashMap::default();
16351        let mut expected = ExpectedTokens::default();
16352        let outcomes = parser.recognize_state_fast(
16353            &atn,
16354            FastRecognizeRequest {
16355                state_number: 1,
16356                stop_state: 2,
16357                index: 0,
16358                rule_start_index: 0,
16359                decision_start_index: None,
16360                precedence: 0,
16361                depth: 0,
16362                recovery_symbols: parser.empty_recovery_symbols(),
16363                recovery_state: None,
16364            },
16365            FastRecognizeScratch {
16366                predicate_context: None,
16367                visiting: &mut visiting,
16368                memo: &mut memo,
16369                expected: &mut expected,
16370                native_depth: 0,
16371            },
16372        );
16373
16374        assert!(outcomes.is_empty());
16375        assert_eq!(memo.len(), 1);
16376        assert!(memo.values().next().expect("memo entry").is_empty());
16377
16378        parser.clean_memo_mode = CleanMemoMode::Sparse;
16379        visiting.clear();
16380        memo.clear();
16381        expected = ExpectedTokens::default();
16382        let sparse_outcomes = parser.recognize_state_fast(
16383            &atn,
16384            FastRecognizeRequest {
16385                state_number: 1,
16386                stop_state: 2,
16387                index: 0,
16388                rule_start_index: 0,
16389                decision_start_index: None,
16390                precedence: 0,
16391                depth: 0,
16392                recovery_symbols: parser.empty_recovery_symbols(),
16393                recovery_state: None,
16394            },
16395            FastRecognizeScratch {
16396                predicate_context: None,
16397                visiting: &mut visiting,
16398                memo: &mut memo,
16399                expected: &mut expected,
16400                native_depth: 0,
16401            },
16402        );
16403
16404        assert!(sparse_outcomes.is_empty());
16405        assert!(memo.is_empty());
16406    }
16407
16408    #[test]
16409    fn wildcard_matches_non_eof_only() {
16410        let mut parser = mini_parser(vec![
16411            TestToken::new(1).with_text("x"),
16412            TestToken::eof("parser-test", 1, 1, 1),
16413        ]);
16414        let matched = parser.match_wildcard().expect("wildcard");
16415        assert_eq!(parser.node(matched).text(), "x");
16416        assert!(parser.match_wildcard().is_err());
16417    }
16418
16419    #[test]
16420    fn add_parse_child_records_match_even_without_tree_building() {
16421        // `sync_decision`'s "is the current context empty" flag must reflect real
16422        // matches, not parse-tree children: when `build_parse_trees(false)`,
16423        // `children` stays empty but `has_matched_child` must still flip so nested
16424        // recovery does not wrongly suppress single-token deletion.
16425        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16426        let token = TestToken::new(1).with_text("x");
16427
16428        parser.set_build_parse_trees(false);
16429        let mut ctx = ParserRuleContext::new(0, 0);
16430        assert!(!ctx.has_matched_child());
16431        let child = parser.terminal_tree(token.id);
16432        parser.add_parse_child(&mut ctx, child);
16433        // Tree building is off, so no child is stored...
16434        assert_eq!(ctx.child_count(), 0);
16435        assert_eq!(parser.parse_tree_storage().node_count(), 0);
16436        // ...but the match is recorded, so the context is no longer "empty".
16437        assert!(ctx.has_matched_child());
16438
16439        // With tree building on, the child is stored and the match is recorded.
16440        parser.set_build_parse_trees(true);
16441        let mut ctx = ParserRuleContext::new(0, 0);
16442        let child = parser.terminal_tree(token.id);
16443        parser.add_parse_child(&mut ctx, child);
16444        assert_eq!(ctx.child_count(), 1);
16445        assert!(ctx.has_matched_child());
16446    }
16447
16448    #[test]
16449    fn disabled_tree_building_does_not_grow_flat_storage() {
16450        let mut parser = mini_parser(vec![
16451            TestToken::new(1).with_text("x"),
16452            TestToken::new(1).with_text("y"),
16453            TestToken::eof("parser-test", 2, 1, 2),
16454        ]);
16455        parser.set_build_parse_trees(false);
16456        let mut context = ParserRuleContext::new(0, -1);
16457
16458        for _ in 0..2 {
16459            let child = parser.match_token(1).expect("token should match");
16460            parser.add_parse_child(&mut context, child);
16461        }
16462        let current = parser.input.lt_id(1).expect("EOF token");
16463        let error = parser.error_tree(current);
16464        parser.add_parse_child(&mut context, error);
16465        let root = parser.rule_node(context);
16466
16467        assert_eq!(
16468            parser.parse_tree_storage().stats(),
16469            ParseTreeStats::default()
16470        );
16471        assert!(
16472            parser
16473                .parse_tree_storage()
16474                .node(parser.token_store(), root)
16475                .is_none(),
16476            "the no-tree sentinel must not resolve to stored data"
16477        );
16478    }
16479
16480    #[test]
16481    fn disabled_tree_building_skips_recognition_rule_node_storage() {
16482        let atn = ordinary_star_loop_atn();
16483        let mut parser = mini_parser(repeated_x_tokens(3));
16484        parser.set_build_parse_trees(false);
16485
16486        parser
16487            .parse_atn_rule(&atn, 0)
16488            .expect("ordinary repetition should parse without a tree");
16489
16490        assert_eq!(parser.input.index(), 3);
16491        assert!(parser.recognition_arena.nodes.is_empty());
16492        assert!(parser.recognition_arena.seq_links.is_empty());
16493        assert!(parser.recognition_arena.deferred_nodes.is_empty());
16494        assert!(parser.recognition_arena.deferred_rules.is_empty());
16495        assert!(!parser.fast_token_nodes_enabled);
16496        assert!(parser.fast_recognize_scratch.memo.is_empty());
16497    }
16498
16499    #[test]
16500    fn parser_interprets_simple_atn_rule() {
16501        let atn = token_then_eof_atn();
16502        let mut parser = mini_parser(vec![
16503            TestToken::new(1).with_text("x"),
16504            TestToken::eof("parser-test", 1, 1, 1),
16505        ]);
16506
16507        let tree = parser
16508            .parse_atn_rule(&atn, 0)
16509            .expect("artificial parser rule should parse");
16510        assert_eq!(parser.node(tree).text(), "x<EOF>");
16511        assert_eq!(parser.number_of_syntax_errors(), 0);
16512        assert_eq!(
16513            parser
16514                .node(tree)
16515                .first_rule_stop(0)
16516                .expect("rule should stop at EOF")
16517                .token_type(),
16518            TOKEN_EOF
16519        );
16520
16521        let mut parser = mini_parser(vec![
16522            TestToken::new(1).with_text("x"),
16523            TestToken::eof("parser-test", 1, 1, 1),
16524        ]);
16525        let (tree, actions) = parser
16526            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16527            .expect("runtime-option parser rule should parse");
16528        assert!(actions.is_empty());
16529        assert_eq!(
16530            parser
16531                .node(tree)
16532                .first_rule_stop(0)
16533                .expect("rule should stop at EOF")
16534                .token_type(),
16535            TOKEN_EOF
16536        );
16537    }
16538
16539    #[test]
16540    fn runtime_options_default_ignores_noop_action_transitions() {
16541        let atn = noop_action_then_token_then_eof_atn();
16542        let mut parser = mini_parser(vec![
16543            TestToken::new(1).with_text("x"),
16544            TestToken::eof("parser-test", 1, 1, 1),
16545        ]);
16546
16547        let (tree, actions) = parser
16548            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16549            .expect("no-op parser action should not force action replay");
16550
16551        assert_eq!(parser.node(tree).text(), "x<EOF>");
16552        assert!(
16553            actions.is_empty(),
16554            "action_index=None transitions are ANTLR metadata, not replay actions"
16555        );
16556        assert_eq!(parser.number_of_syntax_errors(), 0);
16557    }
16558
16559    #[test]
16560    fn parser_exposes_buffered_token_stream_after_parse() {
16561        let atn = token_then_eof_atn();
16562        let mut parser = mini_parser(vec![
16563            TestToken::new(1).with_text("x"),
16564            TestToken::eof("parser-test", 1, 1, 1),
16565        ]);
16566
16567        let tree = parser
16568            .parse_atn_rule(&atn, 0)
16569            .expect("artificial parser rule should parse");
16570        assert_eq!(parser.node(tree).text(), "x<EOF>");
16571
16572        let stream = parser.token_stream();
16573        let source_index_after_parse = stream.token_source().index;
16574        let buffered = stream.tokens().collect::<Vec<_>>();
16575        assert_eq!(buffered.len(), 2);
16576        assert_eq!(buffered[0].text(), Some("x"));
16577        assert_eq!(buffered[0].token_id().index(), 0);
16578        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
16579        assert_eq!(stream.token_source().index, source_index_after_parse);
16580        drop(buffered);
16581
16582        let stream = parser.into_token_stream();
16583        assert_eq!(stream.token_source().index, source_index_after_parse);
16584        assert_eq!(
16585            stream.tokens().next().expect("first token").text(),
16586            Some("x")
16587        );
16588        assert_eq!(
16589            stream.tokens().nth(1).expect("EOF token").token_type(),
16590            TOKEN_EOF
16591        );
16592    }
16593
16594    #[test]
16595    fn parsed_file_exposes_all_buffered_tokens() {
16596        let atn = token_then_eof_atn();
16597        let mut parser = mini_parser(vec![
16598            TestToken::new(99)
16599                .with_text(" comment")
16600                .with_channel(HIDDEN_CHANNEL),
16601            TestToken::new(1).with_text("x"),
16602            TestToken::eof("parser-test", 9, 1, 9),
16603        ]);
16604
16605        let tree = parser
16606            .parse_atn_rule(&atn, 0)
16607            .expect("artificial parser rule should parse");
16608        let parsed = parser.into_parsed_file(tree);
16609
16610        // Snapshot the full buffered stream — hidden-channel comment, default-channel token, EOF —
16611        // as (type, channel, text) triples; contents make the count self-evident.
16612        insta::assert_debug_snapshot!(
16613            "parsed_file_exposes_all_buffered_tokens",
16614            parsed
16615                .tokens()
16616                .iter()
16617                .map(|token| (token.token_type(), token.channel(), token.text()))
16618                .collect::<Vec<_>>()
16619        );
16620        assert_eq!(parsed.tokens().into_iter().count(), 3);
16621    }
16622
16623    #[test]
16624    fn parser_syntax_error_count_tracks_interpreted_recovery() {
16625        let atn = token_then_eof_atn();
16626        let mut parser = mini_parser(vec![
16627            TestToken::new(1).with_text("x"),
16628            TestToken::new(2).with_text("y"),
16629            TestToken::eof("parser-test", 2, 1, 2),
16630        ]);
16631
16632        let tree = parser
16633            .parse_atn_rule(&atn, 0)
16634            .expect("invalid token should recover into an error node");
16635
16636        assert_eq!(parser.number_of_syntax_errors(), 1);
16637        assert_eq!(
16638            parser
16639                .node(tree)
16640                .first_error_token()
16641                .expect("recovery should embed an error token")
16642                .text(),
16643            Some("y")
16644        );
16645    }
16646
16647    #[test]
16648    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
16649        let atn = token_then_eof_atn();
16650        let mut parser = mini_parser(vec![
16651            TestToken::new(2).with_text("y"),
16652            TestToken::eof("parser-test", 1, 1, 1),
16653        ]);
16654
16655        let error = parser
16656            .parse_atn_rule(&atn, 0)
16657            .expect_err("start-rule mismatch should remain a parser error");
16658
16659        assert_eq!(parser.number_of_syntax_errors(), 1);
16660        assert!(matches!(error, AntlrError::ParserError { .. }));
16661    }
16662
16663    #[test]
16664    fn adaptive_direct_rule_uses_simulator_decision() {
16665        let atn = two_alt_decision_atn();
16666        let mut simulator = ParserAtnSimulator::new(&atn);
16667        let mut parser = mini_parser(vec![
16668            TestToken::new(2).with_text("y"),
16669            TestToken::eof("parser-test", 1, 1, 1),
16670        ]);
16671
16672        let tree = parser
16673            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
16674            .expect("direct adaptive rule should parse");
16675
16676        assert_eq!(parser.node(tree).text(), "y");
16677        assert_eq!(parser.input.index(), 1);
16678    }
16679
16680    #[test]
16681    fn adaptive_direct_rule_restores_input_on_fallback() {
16682        let atn = predicate_after_token_atn();
16683        let mut simulator = ParserAtnSimulator::new(&atn);
16684        let mut parser = mini_parser(vec![
16685            TestToken::new(1).with_text("x"),
16686            TestToken::new(2).with_text("y"),
16687            TestToken::eof("parser-test", 2, 1, 2),
16688        ]);
16689
16690        let tree = parser
16691            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
16692            .expect("fallback recognizer should parse");
16693
16694        assert_eq!(parser.node(tree).text(), "xy");
16695        assert_eq!(parser.input.index(), 2);
16696        let stats = parser.parse_tree_storage().stats();
16697        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
16698        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
16699        assert_eq!(stats.scratch_links, 0);
16700    }
16701
16702    #[test]
16703    fn unknown_predicate_policy_defaults_to_assume_true() {
16704        let atn = predicate_after_token_atn();
16705        let mut parser = mini_parser(vec![
16706            TestToken::new(1).with_text("x"),
16707            TestToken::new(2).with_text("y"),
16708            TestToken::eof("parser-test", 2, 1, 2),
16709        ]);
16710
16711        let (tree, _) = parser
16712            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16713            .expect("unknown predicate should pass under the default policy");
16714
16715        assert_eq!(parser.node(tree).text(), "xy");
16716        assert_eq!(parser.number_of_syntax_errors(), 0);
16717    }
16718
16719    #[test]
16720    fn private_context_alt_tracking_keeps_fast_predicate_recognition() {
16721        let atn = predicate_gated_same_lookahead_atn([0, 1]);
16722        let mut parser = mini_parser(vec![
16723            TestToken::new(1).with_text("x"),
16724            TestToken::eof("parser-test", 1, 1, 1),
16725        ]);
16726
16727        let (tree, _) = parser
16728            .parse_atn_rule_with_runtime_options(
16729                &atn,
16730                0,
16731                ParserRuntimeOptions {
16732                    predicates: &[
16733                        (0, 0, ParserPredicate::False),
16734                        (0, 1, ParserPredicate::True),
16735                    ],
16736                    track_context_alt_numbers: true,
16737                    ..ParserRuntimeOptions::default()
16738                },
16739            )
16740            .expect("the second predicate-gated alternative should match");
16741
16742        let root = parser.node(tree).as_rule().expect("entry result is a rule");
16743        insta::assert_debug_snapshot!(
16744            "private_context_alt_tracking_keeps_fast_predicate_recognition",
16745            (root.alt_number(), root.context_alt_number(), root.text())
16746        );
16747        assert_eq!(parser.number_of_syntax_errors(), 0);
16748        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
16749        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
16750    }
16751
16752    #[test]
16753    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
16754        // A generated parent may record an unknown-predicate coordinate, then
16755        // descend into an interpreted child. The child's interpreter entry must
16756        // not wipe the parent's recorded hit before the top-level surfaces it.
16757        let atn = token_then_eof_atn();
16758        let mut parser = mini_parser(vec![
16759            TestToken::new(1).with_text("x"),
16760            TestToken::eof("parser-test", 1, 1, 1),
16761        ]);
16762
16763        // Simulate the parent having recorded a fail-loud coordinate.
16764        parser.unknown_predicate_hits.push((7, 3));
16765
16766        // Run an interpreted child parse that records no coordinate of its own.
16767        parser
16768            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16769            .expect("child rule parses");
16770
16771        // The parent's coordinate must still be present for the top-level entry.
16772        let error = parser
16773            .take_unknown_semantic_error()
16774            .expect("parent's recorded coordinate must survive the nested interpreted parse");
16775        let AntlrError::Unsupported(message) = error else {
16776            panic!("expected AntlrError::Unsupported, got {error:?}");
16777        };
16778        assert!(message.contains("pred_index=3"), "message: {message}");
16779    }
16780
16781    #[test]
16782    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
16783        let atn = predicate_after_token_atn();
16784        let mut parser = mini_parser(vec![
16785            TestToken::new(1).with_text("x"),
16786            TestToken::new(2).with_text("y"),
16787            TestToken::eof("parser-test", 2, 1, 2),
16788        ]);
16789
16790        let result = parser.parse_atn_rule_with_runtime_options(
16791            &atn,
16792            0,
16793            ParserRuntimeOptions {
16794                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16795                ..ParserRuntimeOptions::default()
16796            },
16797        );
16798
16799        assert!(
16800            result.is_err(),
16801            "the only path is predicate-guarded, so assume-false must fail the parse"
16802        );
16803    }
16804
16805    #[test]
16806    fn predicate_failure_message_keeps_semantic_recovery_path() {
16807        let atn = predicate_after_token_atn();
16808        let mut parser = mini_parser(vec![
16809            TestToken::new(1).with_text("x"),
16810            TestToken::new(2).with_text("y"),
16811            TestToken::eof("parser-test", 2, 1, 2),
16812        ]);
16813
16814        let (tree, _) = parser
16815            .parse_atn_rule_with_runtime_options(
16816                &atn,
16817                0,
16818                ParserRuntimeOptions {
16819                    predicates: &[(
16820                        0,
16821                        0,
16822                        ParserPredicate::FalseWithMessage {
16823                            message: "predicate rejected input",
16824                        },
16825                    )],
16826                    ..ParserRuntimeOptions::default()
16827                },
16828            )
16829            .expect("failure-message predicates recover through the semantic interpreter");
16830
16831        assert_eq!(parser.node(tree).text(), "xy");
16832        assert_eq!(parser.number_of_syntax_errors(), 1);
16833        assert!(
16834            parser.fast_predicate_cache.is_empty(),
16835            "failure-message predicates need the semantic interpreter's recovery outcome"
16836        );
16837    }
16838
16839    #[test]
16840    fn unknown_predicate_policy_error_names_the_coordinate() {
16841        let atn = predicate_after_token_atn();
16842        let mut parser = mini_parser(vec![
16843            TestToken::new(1).with_text("x"),
16844            TestToken::new(2).with_text("y"),
16845            TestToken::eof("parser-test", 2, 1, 2),
16846        ]);
16847
16848        let error = parser
16849            .parse_atn_rule_with_runtime_options(
16850                &atn,
16851                0,
16852                ParserRuntimeOptions {
16853                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16854                    ..ParserRuntimeOptions::default()
16855                },
16856            )
16857            .expect_err("evaluating an unknown predicate under Error policy must fail");
16858
16859        let AntlrError::Unsupported(message) = error else {
16860            panic!("expected AntlrError::Unsupported, got {error:?}");
16861        };
16862        assert!(
16863            message.contains("unsupported semantic predicate"),
16864            "message should name the failure class: {message}"
16865        );
16866        assert!(
16867            message.contains("pred_index=0"),
16868            "message should carry the coordinate: {message}"
16869        );
16870    }
16871
16872    #[test]
16873    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
16874        // A parser reused after a fail-loud parse must not carry the old
16875        // coordinates into a later parse. The fail-loud return keeps the hits
16876        // (so a generated parent can surface a recovered child's coordinate),
16877        // and the next parse's entry stashes/replaces them, so a subsequent
16878        // clean parse surfaces no stale error.
16879        let atn = predicate_after_token_atn();
16880        let mut parser = mini_parser(vec![
16881            TestToken::new(1).with_text("x"),
16882            TestToken::new(2).with_text("y"),
16883            TestToken::eof("parser-test", 2, 1, 2),
16884        ]);
16885
16886        parser
16887            .parse_atn_rule_with_runtime_options(
16888                &atn,
16889                0,
16890                ParserRuntimeOptions {
16891                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16892                    ..ParserRuntimeOptions::default()
16893                },
16894            )
16895            .expect_err("first parse fails loud under the Error policy");
16896
16897        // The failed parse kept its coordinate on the parser (so a generated
16898        // parent could surface a recovered child). A top-level reuse resets the
16899        // hits — generated parsers call `reset_unknown_semantic_hits` at their
16900        // public entry; direct interpreter-API callers do the same.
16901        parser.reset_unknown_semantic_hits();
16902        assert!(
16903            parser.take_unknown_semantic_error().is_none(),
16904            "reset must drop stale unknown-predicate coordinates before a reused parse"
16905        );
16906    }
16907
16908    #[derive(Debug, Default)]
16909    struct RecordingHooks {
16910        predicates: Vec<(usize, usize, usize, Option<String>)>,
16911        actions: Vec<(usize, String, Option<String>)>,
16912        action_trees: Vec<Option<String>>,
16913    }
16914
16915    impl SemanticHooks for RecordingHooks {
16916        fn sempred<S>(
16917            &mut self,
16918            ctx: &mut ParserSemCtx<'_, S>,
16919            rule_index: usize,
16920            pred_index: usize,
16921        ) -> Option<bool>
16922        where
16923            S: TokenSource,
16924        {
16925            self.predicates.push((
16926                ctx.input_index(),
16927                rule_index,
16928                pred_index,
16929                ctx.token_text(1)
16930                    .and_then(|token| token.text().map(str::to_owned)),
16931            ));
16932            Some(true)
16933        }
16934
16935        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
16936        where
16937            S: TokenSource,
16938        {
16939            self.actions.push((
16940                action.source_state(),
16941                ctx.action_text(),
16942                ctx.rule_name().map(str::to_owned),
16943            ));
16944            self.action_trees.push(ctx.tree().map(Node::text));
16945            true
16946        }
16947    }
16948
16949    #[derive(Debug, Default)]
16950    struct RejectingPredicateHooks {
16951        predicates: Vec<(usize, usize, usize, Option<String>)>,
16952    }
16953
16954    impl SemanticHooks for RejectingPredicateHooks {
16955        fn sempred<S>(
16956            &mut self,
16957            ctx: &mut ParserSemCtx<'_, S>,
16958            rule_index: usize,
16959            pred_index: usize,
16960        ) -> Option<bool>
16961        where
16962            S: TokenSource,
16963        {
16964            self.predicates.push((
16965                ctx.input_index(),
16966                rule_index,
16967                pred_index,
16968                ctx.token_text(1)
16969                    .and_then(|token| token.text().map(str::to_owned)),
16970            ));
16971            Some(false)
16972        }
16973    }
16974
16975    #[test]
16976    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
16977        let atn = predicate_gated_same_lookahead_atn([0, 0]);
16978        let mut parser = mini_parser_with_hooks(
16979            vec![
16980                TestToken::new(1).with_text("x"),
16981                TestToken::eof("parser-test", 1, 1, 1),
16982            ],
16983            RecordingHooks::default(),
16984        );
16985
16986        let (tree, _) = parser
16987            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16988            .expect("both alternatives share one replay-safe predicate result");
16989
16990        assert_eq!(parser.node(tree).text(), "x<EOF>");
16991        assert_eq!(
16992            parser.semantic_hooks.predicates,
16993            vec![(0, 0, 0, Some("x".to_owned()))]
16994        );
16995        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
16996    }
16997
16998    #[test]
16999    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
17000        let atn = predicate_after_token_atn();
17001        let mut parser = mini_parser_with_hooks(
17002            vec![
17003                TestToken::new(1).with_text("x"),
17004                TestToken::new(2).with_text("y"),
17005                TestToken::eof("parser-test", 2, 1, 2),
17006            ],
17007            RecordingHooks::default(),
17008        );
17009
17010        let (tree, _) = parser
17011            .parse_atn_rule_with_runtime_options(
17012                &atn,
17013                0,
17014                ParserRuntimeOptions {
17015                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17016                    ..ParserRuntimeOptions::default()
17017                },
17018            )
17019            .expect("hook supplies the missing predicate result");
17020
17021        assert_eq!(parser.node(tree).text(), "xy");
17022        assert_eq!(
17023            parser.semantic_hooks.predicates,
17024            vec![(1, 0, 0, Some("y".to_owned()))]
17025        );
17026        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
17027    }
17028
17029    #[test]
17030    fn runtime_options_default_preserves_semantic_hook_predicates() {
17031        let atn = predicate_after_token_atn();
17032        let mut parser = mini_parser_with_hooks(
17033            vec![
17034                TestToken::new(1).with_text("x"),
17035                TestToken::new(2).with_text("y"),
17036                TestToken::eof("parser-test", 2, 1, 2),
17037            ],
17038            RejectingPredicateHooks::default(),
17039        );
17040
17041        let result =
17042            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
17043
17044        assert!(
17045            result.is_err(),
17046            "default runtime options must not bypass semantic hooks for predicate ATNs"
17047        );
17048        assert_eq!(
17049            parser.semantic_hooks.predicates,
17050            vec![(1, 0, 0, Some("y".to_owned()))]
17051        );
17052        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
17053    }
17054
17055    #[test]
17056    fn semantic_hook_handles_committed_parser_action() {
17057        let atn = token_then_eof_atn();
17058        let mut parser = mini_parser_with_hooks(
17059            vec![
17060                TestToken::new(1).with_text("x"),
17061                TestToken::eof("parser-test", 1, 1, 1),
17062            ],
17063            RecordingHooks::default(),
17064        );
17065        let (tree, _) = parser
17066            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17067            .expect("rule parses before action hook is tested");
17068
17069        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17070        assert_eq!(
17071            parser.semantic_hooks.actions,
17072            vec![(42, "x".to_owned(), Some("s".to_owned()))]
17073        );
17074        assert_eq!(
17075            parser.semantic_hooks.action_trees,
17076            [Some("x<EOF>".to_owned())]
17077        );
17078    }
17079
17080    #[test]
17081    fn unhandled_committed_action_fails_loud_under_error_policy() {
17082        // An action offered to the hook that no hook handles (returns false)
17083        // must be recorded and surfaced as `AntlrError::Unsupported` under the
17084        // Error policy, so a `hook`-disposed action is not silently dropped.
17085        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17086        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17087        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
17088
17089        // DecliningHooks::action returns false (unhandled).
17090        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17091
17092        let error = parser
17093            .take_unknown_semantic_error()
17094            .expect("an unhandled committed action under Error policy must fail loud");
17095        let AntlrError::Unsupported(message) = error else {
17096            panic!("expected AntlrError::Unsupported, got {error:?}");
17097        };
17098        assert!(
17099            message.contains("unhandled semantic action") && message.contains("state=42"),
17100            "message should name the dropped action coordinate: {message}"
17101        );
17102
17103        // Under the default (assume-true) policy the same miss is not recorded.
17104        let mut lenient =
17105            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17106        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
17107        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17108        assert!(lenient.take_unknown_semantic_error().is_none());
17109    }
17110
17111    #[test]
17112    fn translated_predicate_is_unaffected_by_error_policy() {
17113        let atn = predicate_after_token_atn();
17114        let mut parser = mini_parser(vec![
17115            TestToken::new(1).with_text("x"),
17116            TestToken::new(2).with_text("y"),
17117            TestToken::eof("parser-test", 2, 1, 2),
17118        ]);
17119
17120        let (tree, _) = parser
17121            .parse_atn_rule_with_runtime_options(
17122                &atn,
17123                0,
17124                ParserRuntimeOptions {
17125                    predicates: &[(0, 0, ParserPredicate::True)],
17126                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17127                    ..ParserRuntimeOptions::default()
17128                },
17129            )
17130            .expect("a predicate covered by the table is not an unknown coordinate");
17131
17132        assert_eq!(parser.node(tree).text(), "xy");
17133    }
17134
17135    /// Hooks that decline (`None`) must fall through to the configured policy
17136    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
17137    /// legacy table path. Regression for the `unwrap_or(false)` that silently
17138    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
17139    fn hook_predicate_semantics() -> ParserSemantics {
17140        let mut ir = SemIr::new();
17141        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
17142        ParserSemantics {
17143            ir,
17144            predicates: vec![ParserSemanticPredicate {
17145                rule_index: 0,
17146                pred_index: 0,
17147                expr,
17148                failure_message: None,
17149            }],
17150            actions: Vec::new(),
17151        }
17152    }
17153
17154    #[derive(Debug, Default)]
17155    struct DecliningHooks;
17156
17157    impl SemanticHooks for DecliningHooks {}
17158
17159    #[test]
17160    fn semir_hook_none_falls_through_to_assume_true() {
17161        let atn = predicate_after_token_atn();
17162        let semantics = hook_predicate_semantics();
17163        let mut parser = mini_parser_with_hooks(
17164            vec![
17165                TestToken::new(1).with_text("x"),
17166                TestToken::new(2).with_text("y"),
17167                TestToken::eof("parser-test", 2, 1, 2),
17168            ],
17169            DecliningHooks,
17170        );
17171
17172        let (tree, _) = parser
17173            .parse_atn_rule_with_runtime_options(
17174                &atn,
17175                0,
17176                ParserRuntimeOptions {
17177                    semantics: Some(&semantics),
17178                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
17179                    ..ParserRuntimeOptions::default()
17180                },
17181            )
17182            .expect("a declined SemIR hook must pass under assume-true");
17183
17184        assert_eq!(parser.node(tree).text(), "xy");
17185    }
17186
17187    #[test]
17188    fn semir_hook_none_falls_through_to_assume_false() {
17189        let atn = predicate_after_token_atn();
17190        let semantics = hook_predicate_semantics();
17191        let mut parser = mini_parser_with_hooks(
17192            vec![
17193                TestToken::new(1).with_text("x"),
17194                TestToken::new(2).with_text("y"),
17195                TestToken::eof("parser-test", 2, 1, 2),
17196            ],
17197            DecliningHooks,
17198        );
17199
17200        let result = parser.parse_atn_rule_with_runtime_options(
17201            &atn,
17202            0,
17203            ParserRuntimeOptions {
17204                semantics: Some(&semantics),
17205                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17206                ..ParserRuntimeOptions::default()
17207            },
17208        );
17209
17210        assert!(
17211            result.is_err(),
17212            "a declined SemIR hook must fail the only guarded path under assume-false"
17213        );
17214    }
17215
17216    #[test]
17217    fn semir_hook_none_records_coordinate_under_error_policy() {
17218        let atn = predicate_after_token_atn();
17219        let semantics = hook_predicate_semantics();
17220        let mut parser = mini_parser_with_hooks(
17221            vec![
17222                TestToken::new(1).with_text("x"),
17223                TestToken::new(2).with_text("y"),
17224                TestToken::eof("parser-test", 2, 1, 2),
17225            ],
17226            DecliningHooks,
17227        );
17228
17229        let error = parser
17230            .parse_atn_rule_with_runtime_options(
17231                &atn,
17232                0,
17233                ParserRuntimeOptions {
17234                    semantics: Some(&semantics),
17235                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17236                    ..ParserRuntimeOptions::default()
17237                },
17238            )
17239            .expect_err("a declined SemIR hook under Error policy must fail the parse");
17240
17241        let AntlrError::Unsupported(message) = error else {
17242            panic!("expected AntlrError::Unsupported, got {error:?}");
17243        };
17244        assert!(
17245            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
17246            "message should name the unresolved coordinate: {message}"
17247        );
17248    }
17249
17250    #[test]
17251    fn generated_direct_predicate_honors_installed_policy() {
17252        // The generated recursive-descent path calls
17253        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
17254        // going through `ParserRuntimeOptions`, so the policy must be installed
17255        // via `set_unknown_predicate_policy` (as the generated constructor now
17256        // does). A declining hook must then honor it rather than the default.
17257        let semantics = hook_predicate_semantics();
17258        let context = ParserRuleContext::new(0, -1);
17259
17260        let mut assume_true =
17261            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17262        assert!(
17263            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
17264                &semantics, 0, 0, &context, 0
17265            ),
17266            "default AssumeTrue accepts a declined hook"
17267        );
17268        assert!(assume_true.take_unknown_semantic_error().is_none());
17269
17270        let mut error_policy =
17271            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17272        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17273        assert!(
17274            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
17275                &semantics, 0, 0, &context, 0
17276            ),
17277            "Error policy rejects a declined hook on the generated-direct path"
17278        );
17279        let error = error_policy
17280            .take_unknown_semantic_error()
17281            .expect("Error policy records the unresolved coordinate for the generated path");
17282        let AntlrError::Unsupported(message) = error else {
17283            panic!("expected AntlrError::Unsupported, got {error:?}");
17284        };
17285        assert!(message.contains("pred_index=0"), "message: {message}");
17286    }
17287
17288    #[test]
17289    fn parser_rule_start_skips_leading_hidden_tokens() {
17290        let atn = token_then_eof_atn();
17291        let mut parser = mini_parser(vec![
17292            TestToken::new(99)
17293                .with_text(" ")
17294                .with_channel(HIDDEN_CHANNEL),
17295            TestToken::new(1).with_text("x"),
17296            TestToken::eof("parser-test", 2, 1, 2),
17297        ]);
17298
17299        let tree = parser
17300            .parse_atn_rule(&atn, 0)
17301            .expect("artificial parser rule should parse");
17302        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
17303            panic!("rule node should be present");
17304        };
17305        assert_eq!(
17306            rule.start()
17307                .expect("rule should have a start token")
17308                .token_type(),
17309            1
17310        );
17311    }
17312
17313    #[test]
17314    fn parser_action_after_eof_stops_at_eof_token() {
17315        let atn = eof_then_action_atn();
17316        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
17317
17318        let (_, actions) = parser
17319            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17320            .expect("EOF action rule should parse");
17321
17322        assert_eq!(actions.len(), 1);
17323        assert_eq!(actions[0].stop_index(), Some(0));
17324        assert_eq!(
17325            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
17326            ""
17327        );
17328    }
17329
17330    #[test]
17331    fn after_action_stop_uses_rule_context_stop_not_cursor() {
17332        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
17333        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
17334        // but the rule did not consume it. The @after stop must follow the rule
17335        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
17336        let mut id = TestToken::new(1).with_text("x");
17337        id.set_token_index(0);
17338        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
17339        eof.set_token_index(1);
17340        let mut parser = mini_parser(vec![id.clone(), eof]);
17341        // Advance the cursor onto EOF, as it would be after `a` matched ID.
17342        parser.consume();
17343        assert_eq!(parser.la(1), TOKEN_EOF);
17344
17345        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
17346        // exactly what finish_rule(consumed_eof = false) records.
17347        let mut ctx = ParserRuleContext::new(0, 0);
17348        parser.set_context_stop(
17349            &mut ctx,
17350            parser.token_id_at(0).expect("ID token should be buffered"),
17351        );
17352        let tree = parser.rule_node(ctx);
17353
17354        let current_index = parser.input.index();
17355        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
17356        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
17357        // ...but the tree-aware helper follows the rule context stop (ID).
17358        assert_eq!(
17359            parser.after_action_stop_index_for_tree(tree, current_index),
17360            Some(0)
17361        );
17362    }
17363
17364    #[test]
17365    fn after_action_start_uses_rule_context_start_not_cursor() {
17366        // A rule that begins after leading hidden-channel tokens: the rule context
17367        // start (set by `enter_rule`) is the first visible token, not the raw cursor
17368        // that may still point at the hidden prefix. The @after start must follow
17369        // the context start so `$start`/`$text` excludes the hidden prefix.
17370        let mut parser = mini_parser(vec![
17371            TestToken::new(9)
17372                .with_text(" ")
17373                .with_channel(HIDDEN_CHANNEL),
17374            TestToken::new(9)
17375                .with_text(" ")
17376                .with_channel(HIDDEN_CHANNEL),
17377            TestToken::new(1).with_text("x"),
17378            TestToken::eof("parser-test", 3, 1, 3),
17379        ]);
17380
17381        let mut ctx = ParserRuleContext::new(0, 0);
17382        parser.set_context_start(
17383            &mut ctx,
17384            parser.token_id_at(2).expect("ID token should be buffered"),
17385        );
17386        let tree = parser.rule_node(ctx);
17387
17388        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
17389        // ...but the tree-aware helper follows the rule context start (index 2).
17390        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
17391
17392        // With no rule start recorded, it falls back to the provided index.
17393        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
17394        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
17395    }
17396
17397    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
17398        FastRecognizeOutcome {
17399            index,
17400            consumed_eof,
17401            diagnostics: DiagnosticSeqId::EMPTY,
17402            deferred_nodes: FastDeferredNodeId::EMPTY,
17403            nodes: NodeSeqId(marker),
17404        }
17405    }
17406
17407    #[test]
17408    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
17409        let mut outcomes = vec![
17410            clean_fast_outcome(4, false, 0),
17411            clean_fast_outcome(2, false, 1),
17412            clean_fast_outcome(4, false, 2),
17413            clean_fast_outcome(4, true, 3),
17414            clean_fast_outcome(2, false, 4),
17415        ];
17416        let mut scratch = FastOutcomeDedupScratch::default();
17417
17418        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17419
17420        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
17421        assert_eq!(
17422            outcomes
17423                .iter()
17424                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
17425                .collect::<Vec<_>>(),
17426            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
17427        );
17428        assert!(scratch.dense_words.is_empty());
17429        assert!(scratch.sparse_keys.is_empty());
17430    }
17431
17432    #[test]
17433    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
17434        let mut scratch = FastOutcomeDedupScratch::default();
17435        let mut outcomes = (100..109)
17436            .flat_map(|index| {
17437                [
17438                    clean_fast_outcome(
17439                        index,
17440                        false,
17441                        u32::try_from(index).expect("test index fits in u32"),
17442                    ),
17443                    clean_fast_outcome(index, false, u32::MAX),
17444                ]
17445            })
17446            .collect();
17447
17448        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17449
17450        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
17451        assert_eq!(outcomes.len(), 9);
17452        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
17453        let dense_capacity = scratch.dense_words.capacity();
17454
17455        let mut reused = (1_000..1_009)
17456            .map(|index| {
17457                clean_fast_outcome(
17458                    index,
17459                    false,
17460                    u32::try_from(index).expect("test index fits in u32"),
17461                )
17462            })
17463            .collect();
17464        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
17465
17466        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
17467        assert_eq!(reused.len(), 9);
17468        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
17469    }
17470
17471    #[test]
17472    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
17473        let mut scratch = FastOutcomeDedupScratch::default();
17474        let sparse_indexes = [
17475            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
17476        ];
17477        let mut outcomes = sparse_indexes
17478            .into_iter()
17479            .chain([400_000])
17480            .enumerate()
17481            .map(|(marker, index)| {
17482                clean_fast_outcome(
17483                    index,
17484                    false,
17485                    u32::try_from(marker).expect("test marker fits in u32"),
17486                )
17487            })
17488            .collect();
17489
17490        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17491
17492        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
17493        assert_eq!(outcomes.len(), sparse_indexes.len());
17494        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
17495        let sparse_capacity = scratch.sparse_keys.capacity();
17496
17497        let mut reused = sparse_indexes
17498            .into_iter()
17499            .map(|index| {
17500                clean_fast_outcome(
17501                    index,
17502                    false,
17503                    u32::try_from(index).expect("test index fits in u32"),
17504                )
17505            })
17506            .collect();
17507        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
17508
17509        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
17510        assert_eq!(reused.len(), sparse_indexes.len());
17511        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
17512    }
17513
17514    #[test]
17515    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
17516        let mut scratch = FastOutcomeDedupScratch::default();
17517        scratch
17518            .sparse_keys
17519            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
17520        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
17521        let mut outcomes = (0..9)
17522            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
17523            .collect();
17524
17525        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17526
17527        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
17528        assert!(scratch.sparse_keys.is_empty());
17529        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
17530    }
17531
17532    #[test]
17533    fn fast_outcome_selection_respects_sll_tie_order() {
17534        let mut arena = RecognitionArena::default();
17535        let first = FastRecognizeOutcome {
17536            index: 1,
17537            consumed_eof: false,
17538            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17539                line: 1,
17540                column: 0,
17541                message: "mismatched input 'x'".to_owned(),
17542            }]),
17543            deferred_nodes: FastDeferredNodeId::EMPTY,
17544            nodes: NodeSeqId::EMPTY,
17545        };
17546        let second = FastRecognizeOutcome {
17547            index: first.index,
17548            consumed_eof: first.consumed_eof,
17549            diagnostics: DiagnosticSeqId::EMPTY,
17550            deferred_nodes: FastDeferredNodeId::EMPTY,
17551            nodes: NodeSeqId::EMPTY,
17552        };
17553
17554        let selected = select_best_fast_outcome(
17555            [first, second].into_iter(),
17556            PredictionMode::Sll,
17557            None,
17558            |_| panic!("caller-follow token probe should not run"),
17559            &arena,
17560        )
17561        .expect("one outcome should be selected");
17562        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
17563        let eof_second = FastRecognizeOutcome {
17564            index: second.index,
17565            consumed_eof: true,
17566            diagnostics: DiagnosticSeqId::EMPTY,
17567            deferred_nodes: FastDeferredNodeId::EMPTY,
17568            nodes: NodeSeqId::EMPTY,
17569        };
17570        let selected = select_best_fast_outcome(
17571            [first, eof_second].into_iter(),
17572            PredictionMode::Sll,
17573            None,
17574            |_| panic!("caller-follow token probe should not run"),
17575            &arena,
17576        )
17577        .expect("one outcome should be selected");
17578        assert!(!selected.consumed_eof);
17579        let selected = select_best_fast_outcome(
17580            [first, second].into_iter(),
17581            PredictionMode::Ll,
17582            None,
17583            |_| panic!("caller-follow token probe should not run"),
17584            &arena,
17585        )
17586        .expect("one outcome should be selected");
17587        assert!(selected.diagnostics.is_empty());
17588    }
17589
17590    #[test]
17591    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
17592        let mut arena = RecognitionArena::default();
17593        let first = FastRecognizeOutcome {
17594            index: 3,
17595            consumed_eof: false,
17596            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17597                line: 1,
17598                column: 0,
17599                message: "mismatched input 'x' expecting 'a'".to_owned(),
17600            }]),
17601            deferred_nodes: FastDeferredNodeId::EMPTY,
17602            nodes: NodeSeqId::EMPTY,
17603        };
17604        let same_rank = FastRecognizeOutcome {
17605            index: first.index,
17606            consumed_eof: first.consumed_eof,
17607            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17608                line: 1,
17609                column: 0,
17610                message: "mismatched input 'x' expecting 'b'".to_owned(),
17611            }]),
17612            deferred_nodes: FastDeferredNodeId::EMPTY,
17613            nodes: NodeSeqId::EMPTY,
17614        };
17615        let better_rank = FastRecognizeOutcome {
17616            index: first.index,
17617            consumed_eof: first.consumed_eof,
17618            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17619                line: 1,
17620                column: 0,
17621                message: "missing 'a' at 'x'".to_owned(),
17622            }]),
17623            deferred_nodes: FastDeferredNodeId::EMPTY,
17624            nodes: NodeSeqId::EMPTY,
17625        };
17626        let mut outcomes = vec![first, same_rank, better_rank];
17627
17628        dedupe_fast_outcomes(&mut outcomes, &arena);
17629
17630        assert_eq!(outcomes.len(), 2);
17631        assert_eq!(
17632            arena
17633                .diagnostics(outcomes[0].diagnostics)
17634                .next()
17635                .expect("first diagnostic")
17636                .message,
17637            "mismatched input 'x' expecting 'a'"
17638        );
17639        assert_eq!(
17640            arena
17641                .diagnostics(outcomes[1].diagnostics)
17642                .next()
17643                .expect("second diagnostic")
17644                .message,
17645            "missing 'a' at 'x'"
17646        );
17647    }
17648
17649    #[test]
17650    fn fast_outcome_selection_prefers_generated_caller_follow() {
17651        let arena = RecognitionArena::default();
17652        let earlier = FastRecognizeOutcome {
17653            index: 7,
17654            consumed_eof: false,
17655            diagnostics: DiagnosticSeqId::EMPTY,
17656            deferred_nodes: FastDeferredNodeId::EMPTY,
17657            nodes: NodeSeqId::EMPTY,
17658        };
17659        let later = FastRecognizeOutcome {
17660            index: 8,
17661            consumed_eof: false,
17662            diagnostics: DiagnosticSeqId::EMPTY,
17663            deferred_nodes: FastDeferredNodeId::EMPTY,
17664            nodes: NodeSeqId::EMPTY,
17665        };
17666        let mut follow = TokenBitSet::default();
17667        follow.insert(5);
17668
17669        let selected = select_best_fast_outcome(
17670            [later, earlier].into_iter(),
17671            PredictionMode::Ll,
17672            Some(&follow),
17673            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
17674            &arena,
17675        )
17676        .expect("one outcome should be selected");
17677        assert_eq!(selected.index, 7);
17678
17679        let selected = select_best_fast_outcome(
17680            [later, earlier].into_iter(),
17681            PredictionMode::Ll,
17682            Some(&follow),
17683            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
17684            &arena,
17685        )
17686        .expect("one outcome should be selected");
17687        assert_eq!(selected.index, 8);
17688
17689        let indented_next_statement = FastRecognizeOutcome {
17690            index: 9,
17691            consumed_eof: false,
17692            diagnostics: DiagnosticSeqId::EMPTY,
17693            deferred_nodes: FastDeferredNodeId::EMPTY,
17694            nodes: NodeSeqId::EMPTY,
17695        };
17696        let selected = select_best_fast_outcome(
17697            [indented_next_statement, earlier].into_iter(),
17698            PredictionMode::Ll,
17699            Some(&follow),
17700            |index| {
17701                let is_boundary = index == 7;
17702                let is_boundary_gap = matches!(index, 7 | 8);
17703                (
17704                    if index == 7 { 5 } else { TOKEN_EOF },
17705                    is_boundary,
17706                    is_boundary_gap,
17707                )
17708            },
17709            &arena,
17710        )
17711        .expect("one outcome should be selected");
17712        assert_eq!(selected.index, 7);
17713
17714        let continuation = FastRecognizeOutcome {
17715            index: 10,
17716            consumed_eof: false,
17717            diagnostics: DiagnosticSeqId::EMPTY,
17718            deferred_nodes: FastDeferredNodeId::EMPTY,
17719            nodes: NodeSeqId::EMPTY,
17720        };
17721        let selected = select_best_fast_outcome(
17722            [continuation, earlier].into_iter(),
17723            PredictionMode::Ll,
17724            Some(&follow),
17725            |index| {
17726                let is_boundary = matches!(index, 7 | 9);
17727                (
17728                    if index == 7 { 5 } else { TOKEN_EOF },
17729                    is_boundary,
17730                    is_boundary,
17731                )
17732            },
17733            &arena,
17734        )
17735        .expect("one outcome should be selected");
17736        assert_eq!(selected.index, 10);
17737
17738        let selected = select_best_fast_outcome(
17739            [earlier, later].into_iter(),
17740            PredictionMode::Sll,
17741            Some(&follow),
17742            |_| panic!("caller-follow token probe should not run in SLL mode"),
17743            &arena,
17744        )
17745        .expect("one outcome should be selected");
17746        assert_eq!(selected.index, 8);
17747    }
17748
17749    #[test]
17750    fn caller_follow_boundary_text_requires_separator_shape() {
17751        assert!(is_caller_follow_boundary_text(";"));
17752        assert!(is_caller_follow_boundary_text("\n"));
17753        assert!(is_caller_follow_boundary_text("\r\n  "));
17754        assert!(is_caller_follow_boundary_text(";\n"));
17755        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
17756        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
17757        assert!(!is_caller_follow_boundary_text("identifier"));
17758        assert!(is_caller_follow_boundary_gap_text(" \t "));
17759        assert!(is_caller_follow_boundary_gap_text("\n  "));
17760        assert!(is_caller_follow_boundary_gap_text(";\t"));
17761        assert!(!is_caller_follow_boundary_gap_text(
17762            "\"\"\"line1\nline2\"\"\""
17763        ));
17764        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
17765    }
17766
17767    #[test]
17768    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
17769        let mut parser = mini_parser(vec![
17770            TestToken::new(5).with_text("\n"),
17771            TestToken::new(6)
17772                .with_text("// comment\n")
17773                .with_channel(HIDDEN_CHANNEL),
17774            TestToken::new(1).with_text("x"),
17775            TestToken::eof("parser-test", 1, 2, 0),
17776        ]);
17777
17778        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17779        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
17780        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
17781    }
17782
17783    #[test]
17784    fn caller_follow_token_info_uses_stream_visible_channel() {
17785        let source = Source {
17786            tokens: vec![
17787                TestToken::new(5).with_text("\n").with_channel(2),
17788                TestToken::new(1).with_text("x").with_channel(2),
17789                TestToken::new(6)
17790                    .with_text("// comment\n")
17791                    .with_channel(HIDDEN_CHANNEL),
17792                TestToken::eof("parser-test", 1, 2, 0),
17793            ],
17794            index: 0,
17795        };
17796        let data = RecognizerData::new(
17797            "Mini.g4",
17798            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17799        );
17800        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
17801
17802        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17803        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
17804        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
17805    }
17806
17807    #[test]
17808    fn reset_per_parse_caches_clears_state_expected_token_cache() {
17809        let atn = token_then_eof_atn();
17810        let mut parser = mini_parser(Vec::new());
17811
17812        let _ = parser.cached_state_expected_token_set(&atn, 0);
17813        assert!(!parser.state_expected_token_cache.is_empty());
17814
17815        parser.reset_per_parse_caches();
17816        assert!(parser.state_expected_token_cache.is_empty());
17817    }
17818
17819    #[test]
17820    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
17821        let cyclic = epsilon_cycle_atn();
17822        let acyclic = token_then_eof_atn();
17823        let mut parser = mini_parser(Vec::new());
17824
17825        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17826        assert_eq!(
17827            parser.empty_cycle_cache_atn,
17828            Some(SharedAtnCacheKey::for_atn(&cyclic))
17829        );
17830        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17831
17832        parser.reset_per_parse_caches();
17833        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17834        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17835
17836        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
17837        assert_eq!(
17838            parser.empty_cycle_cache_atn,
17839            Some(SharedAtnCacheKey::for_atn(&acyclic))
17840        );
17841        assert_eq!(parser.empty_cycle_cache[1], Some(false));
17842    }
17843
17844    #[test]
17845    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
17846        let source = Source {
17847            tokens: vec![
17848                TestToken::new(1).with_text("x"),
17849                TestToken::eof("parser-test", 1, 1, 1),
17850            ],
17851            index: 0,
17852        };
17853        let data = RecognizerData::new(
17854            "Mini.g4",
17855            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17856        );
17857        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17858        let expected = ExpectedTokens {
17859            index: Some(0),
17860            symbols: BTreeSet::new(),
17861            no_viable: None,
17862        };
17863
17864        let (_, message) = parser.expected_error_message(0, 0, &expected);
17865
17866        assert_eq!(message, "mismatched input 'x'");
17867    }
17868
17869    #[test]
17870    fn eof_rule_stop_index_points_at_eof_token() {
17871        let source = Source {
17872            tokens: vec![
17873                TestToken::new(1).with_text("x"),
17874                TestToken::eof("parser-test", 1, 1, 1),
17875            ],
17876            index: 0,
17877        };
17878        let data = RecognizerData::new(
17879            "Mini.g4",
17880            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17881        );
17882        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17883
17884        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
17885        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
17886    }
17887
17888    #[test]
17889    fn generated_parser_action_uses_current_rule_stop_boundary() {
17890        let mut parser = mini_parser(vec![
17891            TestToken::new(1).with_text("x"),
17892            TestToken::eof("parser-test", 1, 1, 1),
17893        ]);
17894
17895        parser.match_token(1).expect("token should match");
17896        let action = parser.parser_action_at_current(7, 0, 0, false);
17897        assert_eq!(action.source_state(), 7);
17898        assert_eq!(action.rule_index(), 0);
17899        assert_eq!(action.start_index(), 0);
17900        assert_eq!(action.stop_index(), Some(0));
17901
17902        parser.match_eof().expect("EOF should match");
17903        let action = parser.parser_action_at_current(8, 0, 0, true);
17904        assert_eq!(action.stop_index(), Some(1));
17905    }
17906
17907    #[test]
17908    fn folds_left_recursive_boundary_into_rule_node() {
17909        let mut arena = RecognitionArena::default();
17910        let first = arena.push_node(ArenaRecognizedNode::Token {
17911            token: TokenId::try_from(0).expect("test token ID"),
17912        });
17913        let boundary = arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
17914            rule_index: 1,
17915            alt_number: 3,
17916        });
17917        let second = arena.push_node(ArenaRecognizedNode::Token {
17918            token: TokenId::try_from(1).expect("test token ID"),
17919        });
17920        let mut nodes = NodeSeqId::EMPTY;
17921        for node in [first, boundary, second].into_iter().rev() {
17922            nodes = arena.prepend(nodes, node);
17923        }
17924
17925        let folded = arena.fold_left_recursive_boundaries(nodes);
17926        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
17927
17928        assert_eq!(folded_nodes.len(), 2);
17929        let ArenaRecognizedNode::Rule {
17930            rule_index,
17931            invoking_state,
17932            alt_number,
17933            start_index,
17934            stop_index,
17935            children,
17936            ..
17937        } = arena.node(folded_nodes[0])
17938        else {
17939            panic!("first folded node should be a rule");
17940        };
17941        // The folded rule node's scalar shape (rule/invoking-state/alt/start/stop) is one snapshot;
17942        // child resolution and the sibling identity below stay explicit — a node Debug prints the
17943        // children handle, not the resolved sequence they assert on.
17944        insta::assert_debug_snapshot!(
17945            "folds_left_recursive_boundary_into_rule_node",
17946            (
17947                rule_index,
17948                invoking_state,
17949                alt_number,
17950                start_index,
17951                stop_index
17952            )
17953        );
17954        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
17955        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
17956
17957        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
17958        assert_eq!(
17959            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17960            (4, 3, 1)
17961        );
17962        assert_eq!(
17963            (stats.total_links, stats.live_links, stats.dead_links),
17964            (9, 3, 6)
17965        );
17966    }
17967
17968    #[test]
17969    fn recognition_arena_reports_live_dead_and_retained_capacity() {
17970        let mut arena = RecognitionArena::default();
17971        let token = arena.push_node(ArenaRecognizedNode::Token {
17972            token: TokenId::try_from(0).expect("test token ID"),
17973        });
17974        let extra = arena.push_extra(RecognitionExtra::MissingToken {
17975            token_type: 2,
17976            at_index: 1,
17977            text: "<missing X>".to_owned(),
17978        });
17979        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
17980        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
17981            token: TokenId::try_from(1).expect("test token ID"),
17982        });
17983        let mut live = NodeSeqId::EMPTY;
17984        live = arena.prepend(live, missing);
17985        live = arena.prepend(live, token);
17986        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
17987        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17988            line: 1,
17989            column: 0,
17990            message: "missing X".to_owned(),
17991        }]);
17992        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17993            line: 1,
17994            column: 1,
17995            message: "discarded".to_owned(),
17996        }]);
17997        let deferred_children = arena.deferred_fragment(live);
17998        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
17999            rule_index: 0,
18000            invoking_state: -1,
18001            start_index: 0,
18002            stop_index: Some(1),
18003            deferred_children,
18004            children: NodeSeqId::EMPTY,
18005        });
18006
18007        let stats = arena.stats(live, live_diagnostics);
18008
18009        assert_eq!(
18010            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18011            (3, 2, 1)
18012        );
18013        assert_eq!(
18014            (stats.total_links, stats.live_links, stats.dead_links),
18015            (5, 3, 2)
18016        );
18017        assert_eq!(
18018            (stats.total_extras, stats.live_extras, stats.dead_extras),
18019            (3, 2, 1)
18020        );
18021        assert!(size_of::<SeqLink>() <= 8);
18022        assert!(size_of::<DiagnosticLink>() <= 8);
18023        assert!(size_of::<FastDeferredNode>() <= 12);
18024        assert!(size_of::<FastDeferredRule>() <= 28);
18025        assert!(size_of::<FastRecognizeOutcome>() <= 24);
18026        let capacities = (
18027            stats.node_capacity,
18028            stats.link_capacity,
18029            stats.extra_capacity,
18030        );
18031        let deferred_capacities = (
18032            arena.deferred_nodes.capacity(),
18033            arena.deferred_rules.capacity(),
18034        );
18035
18036        arena.reset();
18037        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
18038        assert_eq!(
18039            (reset.total_nodes, reset.total_links, reset.total_extras),
18040            (0, 0, 0)
18041        );
18042        assert_eq!(
18043            (
18044                reset.node_capacity,
18045                reset.link_capacity,
18046                reset.extra_capacity,
18047            ),
18048            capacities
18049        );
18050        assert!(arena.deferred_nodes.is_empty());
18051        assert!(arena.deferred_rules.is_empty());
18052        assert_eq!(
18053            (
18054                arena.deferred_nodes.capacity(),
18055                arena.deferred_rules.capacity(),
18056            ),
18057            deferred_capacities
18058        );
18059    }
18060
18061    #[test]
18062    fn parser_computes_recognition_arena_stats_on_demand() {
18063        let mut parser = mini_parser(Vec::new());
18064        let live = parser
18065            .recognition_arena
18066            .push_node(ArenaRecognizedNode::Token {
18067                token: TokenId::try_from(0).expect("test token ID"),
18068            });
18069        let discarded = parser
18070            .recognition_arena
18071            .push_node(ArenaRecognizedNode::ErrorToken {
18072                token: TokenId::try_from(1).expect("test token ID"),
18073            });
18074        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
18075        let _discarded_root = parser
18076            .recognition_arena
18077            .prepend(NodeSeqId::EMPTY, discarded);
18078        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
18079
18080        let stats = parser.recognition_arena_stats();
18081
18082        assert_eq!(
18083            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18084            (2, 1, 1)
18085        );
18086        assert_eq!(
18087            (stats.total_links, stats.live_links, stats.dead_links),
18088            (2, 1, 1)
18089        );
18090    }
18091
18092    #[test]
18093    fn recognition_arena_drops_capacity_above_retention_limit() {
18094        let mut storage = Vec::<u8>::with_capacity(4);
18095        storage.extend([1, 2, 3]);
18096
18097        reset_arena_vec(&mut storage, 3);
18098
18099        assert!(storage.is_empty());
18100        assert_eq!(storage.capacity(), 0);
18101    }
18102
18103    #[test]
18104    fn recognition_arena_concatenates_diagnostics_in_source_order() {
18105        let mut arena = RecognitionArena::default();
18106        let prefix = arena.diagnostic_sequence([
18107            ParserDiagnostic {
18108                line: 1,
18109                column: 0,
18110                message: "first".to_owned(),
18111            },
18112            ParserDiagnostic {
18113                line: 1,
18114                column: 1,
18115                message: "second".to_owned(),
18116            },
18117        ]);
18118        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
18119            line: 1,
18120            column: 2,
18121            message: "third".to_owned(),
18122        }]);
18123        let extras_before = arena.extras.len();
18124
18125        let combined = arena.concat_diagnostics(prefix, suffix);
18126        let messages = arena
18127            .diagnostics(combined)
18128            .map(|diagnostic| diagnostic.message.as_str())
18129            .collect::<Vec<_>>();
18130
18131        assert_eq!(messages, ["first", "second", "third"]);
18132        assert_eq!(arena.extras.len(), extras_before);
18133    }
18134
18135    #[test]
18136    fn outcome_ties_keep_later_non_recursive_alternative() {
18137        let arena = RecognitionArena::default();
18138        let first = RecognizeOutcome {
18139            index: 1,
18140            consumed_eof: false,
18141            alt_number: 0,
18142            member_values: BTreeMap::new(),
18143            return_values: BTreeMap::new(),
18144            diagnostics: DiagnosticSeqId::EMPTY,
18145            decisions: Vec::new(),
18146            actions: vec![ParserAction::new(1, 0, 0, None)],
18147            nodes: NodeSeqId::EMPTY,
18148        };
18149        let second = RecognizeOutcome {
18150            actions: vec![ParserAction::new(2, 0, 0, None)],
18151            ..first.clone()
18152        };
18153
18154        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18155            .expect("one outcome should be selected");
18156        assert_eq!(selected.actions[0].source_state(), 2);
18157    }
18158
18159    #[test]
18160    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
18161        let arena = RecognitionArena::default();
18162        let first = RecognizeOutcome {
18163            index: 1,
18164            consumed_eof: false,
18165            alt_number: 0,
18166            member_values: BTreeMap::new(),
18167            return_values: BTreeMap::new(),
18168            diagnostics: DiagnosticSeqId::EMPTY,
18169            decisions: Vec::new(),
18170            actions: vec![ParserAction::new(1, 0, 0, None)],
18171            nodes: NodeSeqId::EMPTY,
18172        };
18173        let second = RecognizeOutcome {
18174            actions: vec![
18175                ParserAction::new(2, 0, 0, None),
18176                ParserAction::new(3, 0, 0, None),
18177            ],
18178            ..first.clone()
18179        };
18180
18181        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
18182            .expect("one outcome should be selected");
18183        assert_eq!(selected.actions.len(), 2);
18184    }
18185
18186    #[test]
18187    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
18188        let arena = RecognitionArena::default();
18189        let first = RecognizeOutcome {
18190            index: 7,
18191            consumed_eof: false,
18192            alt_number: 0,
18193            member_values: BTreeMap::new(),
18194            return_values: BTreeMap::new(),
18195            diagnostics: DiagnosticSeqId::EMPTY,
18196            decisions: vec![1, 0],
18197            actions: vec![
18198                ParserAction::new(23, 2, 2, Some(4)),
18199                ParserAction::new(23, 2, 0, Some(6)),
18200            ],
18201            nodes: NodeSeqId::EMPTY,
18202        };
18203        let second = RecognizeOutcome {
18204            decisions: vec![0, 1],
18205            actions: vec![
18206                ParserAction::new(23, 2, 2, Some(6)),
18207                ParserAction::new(23, 2, 0, Some(6)),
18208            ],
18209            ..first.clone()
18210        };
18211
18212        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18213            .expect("one outcome should be selected");
18214        assert_eq!(selected.actions[0].stop_index(), Some(6));
18215    }
18216
18217    #[test]
18218    fn outcome_ties_keep_first_recursive_tree_shape() {
18219        let mut arena = RecognitionArena::default();
18220        let token = arena.push_node(ArenaRecognizedNode::Token {
18221            token: TokenId::try_from(0).expect("test token ID"),
18222        });
18223        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
18224        let inner = arena.push_node(ArenaRecognizedNode::Rule {
18225            rule_index: 1,
18226            invoking_state: -1,
18227            alt_number: 0,
18228            start_index: 0,
18229            stop_index: Some(0),
18230            return_values: None,
18231            children: token_children,
18232        });
18233        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
18234        let outer = arena.push_node(ArenaRecognizedNode::Rule {
18235            rule_index: 1,
18236            invoking_state: -1,
18237            alt_number: 0,
18238            start_index: 0,
18239            stop_index: Some(0),
18240            return_values: None,
18241            children: inner_children,
18242        });
18243        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
18244        let first = RecognizeOutcome {
18245            index: 1,
18246            consumed_eof: false,
18247            alt_number: 0,
18248            member_values: BTreeMap::new(),
18249            return_values: BTreeMap::new(),
18250            diagnostics: DiagnosticSeqId::EMPTY,
18251            decisions: Vec::new(),
18252            actions: vec![ParserAction::new(1, 0, 0, None)],
18253            nodes: recursive_nodes,
18254        };
18255        let second = RecognizeOutcome {
18256            index: 1,
18257            consumed_eof: false,
18258            alt_number: 0,
18259            member_values: BTreeMap::new(),
18260            return_values: BTreeMap::new(),
18261            diagnostics: DiagnosticSeqId::EMPTY,
18262            decisions: Vec::new(),
18263            actions: vec![ParserAction::new(2, 0, 0, None)],
18264            nodes: recursive_nodes,
18265        };
18266
18267        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18268            .expect("one outcome should be selected");
18269        assert_eq!(selected.actions[0].source_state(), 1);
18270    }
18271
18272    #[test]
18273    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
18274        let mut arena = RecognitionArena::default();
18275        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18276            line: 1,
18277            column: 3,
18278            message: "missing 'Y' at '<EOF>'".to_owned(),
18279        }]);
18280        let first_alt = RecognizeOutcome {
18281            index: 2,
18282            consumed_eof: true,
18283            alt_number: 0,
18284            member_values: BTreeMap::new(),
18285            return_values: BTreeMap::new(),
18286            diagnostics: recovered_diagnostics,
18287            decisions: vec![0],
18288            actions: vec![ParserAction::new(1, 0, 0, None)],
18289            nodes: NodeSeqId::EMPTY,
18290        };
18291        let second_alt = RecognizeOutcome {
18292            diagnostics: DiagnosticSeqId::EMPTY,
18293            decisions: vec![1],
18294            actions: vec![ParserAction::new(2, 0, 0, None)],
18295            ..first_alt.clone()
18296        };
18297
18298        let selected = select_best_outcome(
18299            [second_alt, first_alt].into_iter(),
18300            PredictionMode::Sll,
18301            &arena,
18302        )
18303        .expect("one outcome should be selected");
18304        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
18305        assert_eq!(selected.decisions, [0]);
18306    }
18307}