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, 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/// Whole-rule direct adaptive execution is allowed to give up and fall back to
107/// the existing recognizer. Keep the guard at the same order of magnitude as
108/// speculative recognition so malformed cyclic ATNs cannot spin forever.
109const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
110/// Probe window for deciding whether clean-pass one-outcome memo entries are
111/// reusable enough to keep caching. Large C# parses mostly produce one-shot
112/// entries; small ambiguous Kotlin loops repeatedly hit the same keys.
113const CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT: usize = 4096;
114const CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT: usize = 8;
115
116#[derive(Clone, Copy, Debug, Eq, PartialEq)]
117enum SingleOutcomeMemoMode {
118    Probe,
119    Promote,
120    Sparse,
121}
122
123fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
124    intervals
125        .iter()
126        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
127}
128
129fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
130    let mut symbols = BTreeSet::new();
131    for (start, stop) in intervals {
132        symbols.extend(*start..=*stop);
133    }
134    symbols
135}
136
137fn interval_complement_symbols(
138    intervals: &[(i32, i32)],
139    min_vocabulary: i32,
140    max_vocabulary: i32,
141) -> BTreeSet<i32> {
142    (min_vocabulary..=max_vocabulary)
143        .filter(|symbol| !interval_set_contains(intervals, *symbol))
144        .collect()
145}
146
147#[cfg(feature = "perf-counters")]
148mod perf_counters {
149    use std::cell::Cell;
150    thread_local! {
151        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
152        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
153        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
154        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
155        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
156        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
157        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
158        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
159        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
160        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
161        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
162        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
163        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
164    }
165    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
166        c.with(|v| v.set(v.get() + n));
167    }
168    thread_local! {
169        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
170        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
171        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
172        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
173        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
174        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
175        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
176        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
177        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
178        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
179        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
180    }
181    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
182        [
183            ("rfs_calls", RFS_CALLS.with(Cell::get)),
184            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
185            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
186            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
187            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
188            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
189            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
190            (
191                "outcome_dedupe_inputs",
192                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
193            ),
194            (
195                "outcome_dedupe_removed",
196                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
197            ),
198            (
199                "outcome_dedupe_inline",
200                OUTCOME_DEDUPE_INLINE.with(Cell::get),
201            ),
202            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
203            (
204                "outcome_dedupe_sparse",
205                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
206            ),
207            (
208                "outcome_dedupe_dense_words",
209                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
210            ),
211            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
212            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
213            (
214                "atom_range_transitions",
215                ATOM_RANGE_TRANSITIONS.with(Cell::get),
216            ),
217            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
218            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
219            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
220            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
221            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
222            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
223            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
224            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
225        ]
226    }
227    pub fn reset() {
228        RFS_CALLS.with(|c| c.set(0));
229        RFS_MEMO_HITS.with(|c| c.set(0));
230        RFS_MEMO_MISSES.with(|c| c.set(0));
231        RFS_VISITING_CYCLE.with(|c| c.set(0));
232        MEMO_INSERTED.with(|c| c.set(0));
233        OUTCOMES_PUSHED.with(|c| c.set(0));
234        OUTCOMES_CLONED.with(|c| c.set(0));
235        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
236        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
237        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
238        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
239        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
240        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
241        EPSILON_TRANSITIONS.with(|c| c.set(0));
242        RULE_TRANSITIONS.with(|c| c.set(0));
243        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
244        SINGLE_TRANS_BODY.with(|c| c.set(0));
245        MULTI_TRANS_BODY.with(|c| c.set(0));
246        SINGLE_TRANS_RULE.with(|c| c.set(0));
247        SINGLE_TRANS_ATOM.with(|c| c.set(0));
248        SINGLE_TRANS_OTHER.with(|c| c.set(0));
249        OUTCOMES_RETURN_0.with(|c| c.set(0));
250        OUTCOMES_RETURN_1.with(|c| c.set(0));
251        OUTCOMES_RETURN_N.with(|c| c.set(0));
252    }
253    pub fn dump() {
254        for (name, value) in snapshot() {
255            #[allow(clippy::print_stderr)]
256            {
257                eprintln!("perf {name}={value}");
258            }
259        }
260    }
261}
262
263#[cfg(feature = "perf-counters")]
264pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
265/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
266/// fast recognizer has probed far enough that per-token stream sync dominates.
267/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
268/// switching long inputs to the cheaper filled-stream path before large fanout.
269const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
270/// Parser semantic action reached while recognizing one ATN path.
271///
272/// Generated parsers use `source_state` to dispatch back to the grammar action
273/// rendered for that ATN action transition. The token interval is the current
274/// rule's input span at the action site, which covers common target templates
275/// such as `$text`. Rule-init actions do not have an ATN action source state,
276/// so they are marked separately and may carry an ATN state for expected-token
277/// rendering.
278#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
279pub struct ParserAction {
280    source_state: usize,
281    rule_index: usize,
282    start_index: usize,
283    stop_index: Option<usize>,
284    rule_init: bool,
285    expected_state: Option<usize>,
286}
287
288impl ParserAction {
289    /// Creates an action event for a recognized parser path.
290    pub const fn new(
291        source_state: usize,
292        rule_index: usize,
293        start_index: usize,
294        stop_index: Option<usize>,
295    ) -> Self {
296        Self {
297            source_state,
298            rule_index,
299            start_index,
300            stop_index,
301            rule_init: false,
302            expected_state: None,
303        }
304    }
305
306    /// Creates an action event for a rule-level `@init` action.
307    pub const fn new_rule_init(
308        rule_index: usize,
309        start_index: usize,
310        expected_state: Option<usize>,
311    ) -> Self {
312        Self {
313            source_state: usize::MAX,
314            rule_index,
315            start_index,
316            stop_index: None,
317            rule_init: true,
318            expected_state,
319        }
320    }
321
322    /// ATN state that owns the semantic-action transition.
323    pub const fn source_state(&self) -> usize {
324        self.source_state
325    }
326
327    /// Grammar rule index recorded by the serialized ATN action transition.
328    pub const fn rule_index(&self) -> usize {
329        self.rule_index
330    }
331
332    /// Token-stream index where the active rule began.
333    pub const fn start_index(&self) -> usize {
334        self.start_index
335    }
336
337    /// Last token-stream index consumed before the action was reached.
338    pub const fn stop_index(&self) -> Option<usize> {
339        self.stop_index
340    }
341
342    /// Reports whether this event represents a rule-level `@init` action.
343    pub const fn is_rule_init(&self) -> bool {
344        self.rule_init
345    }
346
347    /// ATN state used to compute expected-token display for this action.
348    pub const fn expected_state(&self) -> Option<usize> {
349        self.expected_state
350    }
351}
352
353/// Runtime view passed to parser semantic hooks.
354///
355/// The context is intentionally read-only with respect to parser structure:
356/// predicates may run speculatively during prediction, and hooks can be called
357/// more than once for paths that are later abandoned. Lookahead methods may
358/// buffer tokens from the underlying token source, matching normal parser
359/// prediction behavior.
360pub struct ParserSemCtx<'a, S>
361where
362    S: TokenSource,
363{
364    input: &'a mut CommonTokenStream<S>,
365    tree_storage: &'a ParseTreeStorage,
366    rule_index: usize,
367    coordinate_index: usize,
368    rule_name: Option<String>,
369    context: Option<&'a ParserRuleContext>,
370    tree: Option<ParseTree>,
371    local_int_arg: Option<(usize, i64)>,
372    member_values: &'a BTreeMap<usize, i64>,
373    action: Option<ParserAction>,
374}
375
376impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
377where
378    S: TokenSource,
379{
380    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381        f.debug_struct("ParserSemCtx")
382            .field("rule_index", &self.rule_index)
383            .field("coordinate_index", &self.coordinate_index)
384            .field("rule_name", &self.rule_name)
385            .field("context", &self.context)
386            .field("tree", &self.tree)
387            .field("local_int_arg", &self.local_int_arg)
388            .field("member_values", &self.member_values)
389            .field("action", &self.action)
390            .finish_non_exhaustive()
391    }
392}
393
394impl<'a, S> ParserSemCtx<'a, S>
395where
396    S: TokenSource,
397{
398    /// Rule index that owns the predicate/action coordinate.
399    #[must_use]
400    pub const fn rule_index(&self) -> usize {
401        self.rule_index
402    }
403
404    /// Rule name that owns the coordinate, when recognizer metadata has it.
405    #[must_use]
406    pub fn rule_name(&self) -> Option<&str> {
407        self.rule_name.as_deref()
408    }
409
410    /// Predicate/action index inside the owning rule. Parser actions keyed only
411    /// by ATN source state report `usize::MAX` here; use [`Self::action`] for
412    /// the stable action event.
413    #[must_use]
414    pub const fn coordinate_index(&self) -> usize {
415        self.coordinate_index
416    }
417
418    /// Current token-stream index.
419    #[must_use]
420    pub fn input_index(&self) -> usize {
421        self.input.index()
422    }
423
424    /// Token type at one-based lookahead/lookbehind offset.
425    pub fn la(&mut self, offset: isize) -> i32 {
426        self.input.la(offset)
427    }
428
429    /// Token at one-based lookahead/lookbehind offset.
430    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
431        self.input.lt(offset)
432    }
433
434    /// Borrowing token view for text inspection at a one-based offset.
435    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
436        self.lt(offset)
437    }
438
439    /// Token at an absolute buffered index, including hidden/custom channels.
440    ///
441    /// Unlike [`Self::lt`], this does not apply the token stream's channel
442    /// filter and does not move its cursor. It is intended for semantic helpers
443    /// such as automatic-semicolon-insertion checks that inspect trivia
444    /// immediately before the current visible token.
445    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
446        self.input.get(index)
447    }
448
449    /// Current generated rule context, when a generated rule predicate supplied
450    /// one.
451    #[must_use]
452    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
453        self.context
454    }
455
456    /// Flat tree storage containing completed children visible to this hook.
457    #[must_use]
458    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
459        self.tree_storage
460    }
461
462    /// Canonical token store used by completed flat-tree nodes.
463    #[must_use]
464    pub const fn token_store(&self) -> &TokenStore {
465        self.input.token_store()
466    }
467
468    /// Completed parse-tree root ID passed to a replayed action hook.
469    #[must_use]
470    pub const fn tree_id(&self) -> Option<NodeId> {
471        self.tree
472    }
473
474    /// Completed parse tree passed to an action hook, if the action is being
475    /// replayed after recognition.
476    #[must_use]
477    pub fn tree(&self) -> Option<Node<'_>> {
478        self.tree
479            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
480    }
481
482    /// Integer local argument visible to this predicate coordinate.
483    #[must_use]
484    pub fn local_int_arg(&self) -> Option<i64> {
485        self.local_int_arg.map(|(_, value)| value)
486    }
487
488    /// Integer member value observed on the current speculative path.
489    #[must_use]
490    pub fn member_int(&self, member: usize) -> Option<i64> {
491        self.member_values.get(&member).copied()
492    }
493
494    /// Parser action event being replayed, when this context belongs to an
495    /// action hook.
496    #[must_use]
497    pub const fn action(&self) -> Option<ParserAction> {
498        self.action
499    }
500
501    /// Text covered by a parser action event.
502    ///
503    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
504    /// EOF the interval ends at the previous *visible* token, so trailing hidden
505    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
506    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
507    /// itself guards `start > stop`, so an empty interval yields `""`.
508    pub fn action_text(&self) -> String {
509        let Some(action) = self.action else {
510            return String::new();
511        };
512        let Some(stop) = action.stop_index() else {
513            return String::new();
514        };
515        let stop = if self
516            .input
517            .get(stop)
518            .is_some_and(|token| token.token_type() == TOKEN_EOF)
519        {
520            let Some(previous) = self.input.previous_visible_token_index(stop) else {
521                return String::new();
522            };
523            previous
524        } else {
525            stop
526        };
527        self.input.text(action.start_index(), stop)
528    }
529}
530
531/// User extension point for parser semantic predicates and actions that the
532/// metadata generator did not translate into built-in runtime metadata.
533///
534/// Returning `None`/`false` says "not handled", so the runtime falls through
535/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
536/// speculative prediction and must be replay-safe.
537pub trait SemanticHooks {
538    /// Whether generated lexers should route lifecycle callbacks through this
539    /// hook object.
540    ///
541    /// User hook implementations opt in by default. [`NoSemanticHooks`]
542    /// overrides this to keep generated lexers on the direct no-extension
543    /// token path.
544    const ENABLES_LEXER_LIFECYCLE: bool = true;
545
546    /// Whether this hook object may observe parser predicate transitions.
547    ///
548    /// Custom hooks default to conservative predicate handling so the fast
549    /// recognizer does not bypass a `sempred` implementation.
550    fn observes_parser_predicates(&self) -> bool {
551        true
552    }
553
554    fn sempred<S>(
555        &mut self,
556        ctx: &mut ParserSemCtx<'_, S>,
557        rule_index: usize,
558        pred_index: usize,
559    ) -> Option<bool>
560    where
561        S: TokenSource,
562    {
563        let _ = (ctx, rule_index, pred_index);
564        None
565    }
566
567    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
568    where
569        S: TokenSource,
570    {
571        let _ = (ctx, action);
572        false
573    }
574
575    fn lexer_sempred<I>(
576        &mut self,
577        ctx: &mut LexerSemCtx<'_, I>,
578        rule_index: usize,
579        pred_index: usize,
580    ) -> Option<bool>
581    where
582        I: CharStream,
583    {
584        let _ = (ctx, rule_index, pred_index);
585        None
586    }
587
588    /// Runs a lexer custom action on the committed lexing path. Returns whether
589    /// the hook handled the action.
590    ///
591    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
592    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
593    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
594    /// queued prefix tokens, just like the closure-based `custom_action` API.
595    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
596    /// borrow, so those mutators are inert there.)
597    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
598    where
599        I: CharStream,
600    {
601        let _ = (ctx, action);
602        false
603    }
604
605    /// Runs after runtime-owned lexer state has been reset for reuse.
606    ///
607    /// Implementations should clear extension-owned transient state here.
608    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
609    where
610        I: CharStream,
611    {
612        let _ = ctx;
613    }
614
615    /// Runs before the runtime returns a queued token or starts a new ATN
616    /// token match.
617    ///
618    /// The callback also runs between internal `skip`/`more` matches, so it
619    /// observes every point where another ATN match may start.
620    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
621    where
622        I: CharStream,
623    {
624        let _ = ctx;
625    }
626
627    /// Runs after the accepted path's portable and custom actions, but before
628    /// the token span is finalized and emitted.
629    ///
630    /// Accepted paths that selected `skip` or `more` are included, and the hook
631    /// may observe or override that pending token type.
632    ///
633    /// This callback has no synthetic ATN coordinate. It therefore also runs
634    /// for accepted rules that contain no action or predicate.
635    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
636    where
637        I: CharStream,
638    {
639        let _ = ctx;
640    }
641
642    /// Observes a token after committed lexer actions and portable commands
643    /// have run and the token has been emitted, immediately before it is
644    /// returned to the token stream.
645    ///
646    /// Hidden and custom-channel tokens are included. `skip` and intermediate
647    /// `more` matches do not produce callbacks.
648    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
649        let _ = token;
650    }
651}
652
653/// Default hook object used by parsers that do not need user-supplied
654/// semantics.
655#[derive(Clone, Copy, Debug, Default)]
656pub struct NoSemanticHooks;
657
658impl SemanticHooks for NoSemanticHooks {
659    const ENABLES_LEXER_LIFECYCLE: bool = false;
660
661    fn observes_parser_predicates(&self) -> bool {
662        false
663    }
664}
665
666/// Parser semantic predicate rendered from a supported target template.
667///
668/// The metadata recognizer evaluates these at the token-stream index where the
669/// predicate transition is reached. Unsupported or absent predicate templates
670/// remain unconditional so existing generated parsers keep their previous
671/// behavior unless the generator opts into this table.
672#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
673pub enum ParserPredicate {
674    True,
675    False,
676    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
677    FalseWithMessage {
678        message: &'static str,
679    },
680    /// Target-template test helper that reports predicate evaluation before
681    /// returning the wrapped boolean value.
682    Invoke {
683        value: bool,
684    },
685    LookaheadTextEquals {
686        offset: isize,
687        text: &'static str,
688    },
689    LookaheadNotEquals {
690        offset: isize,
691        token_type: i32,
692    },
693    /// Checks that the last two consumed visible tokens were adjacent in the
694    /// token stream. Used by C# parser predicates for split operator tokens.
695    TokenPairAdjacent,
696    /// Checks a generated parser context child by rule index and text.
697    ///
698    /// If the child is absent the predicate succeeds, matching target helpers
699    /// that treat incomplete or non-matching contexts as non-restrictive.
700    ContextChildRuleTextNotEquals {
701        rule_index: usize,
702        text: &'static str,
703    },
704    /// Compares the current rule invocation's integer argument with a literal
705    /// value from a supported `ValEquals("$i", "...")` target template.
706    LocalIntEquals {
707        value: i64,
708    },
709    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
710    /// rule invocation's integer argument.
711    LocalIntLessOrEqual {
712        value: i64,
713    },
714    /// Compares a generated parser integer member modulo a literal value.
715    MemberModuloEquals {
716        member: usize,
717        modulus: i64,
718        value: i64,
719        equals: bool,
720    },
721    /// Compares a generated parser integer member with a literal value.
722    MemberEquals {
723        member: usize,
724        value: i64,
725        equals: bool,
726    },
727}
728
729impl ParserPredicate {
730    /// Lowers the legacy predicate metadata variant into `SemIR`.
731    ///
732    /// This is the compatibility adapter for generated parsers produced while
733    /// the runtime still emitted closed enum tables. Newer generated parsers
734    /// emit `SemIR` directly.
735    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
736        match self {
737            Self::True => ir.expr(PExpr::Bool(true)),
738            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
739            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
740            Self::LookaheadTextEquals { offset, text } => {
741                let token = ir.expr(PExpr::TokenText(offset));
742                let text = ir.intern(text);
743                let text = ir.expr(PExpr::Str(text));
744                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
745            }
746            Self::LookaheadNotEquals { offset, token_type } => {
747                let actual = ir.expr(PExpr::La(offset));
748                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
749                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
750            }
751            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
752            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
753                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
754                let expected = ir.intern(text);
755                let expected = ir.expr(PExpr::Str(expected));
756                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
757            }
758            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
759            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
760            Self::MemberModuloEquals {
761                member,
762                modulus,
763                value,
764                equals,
765            } => {
766                if modulus == 0 {
767                    return ir.expr(PExpr::Bool(false));
768                }
769                let member = ir.expr(PExpr::Member(member));
770                let modulus = ir.expr(PExpr::Int(modulus));
771                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
772                let expected = ir.expr(PExpr::Int(value));
773                ir.expr(PExpr::Cmp(
774                    if equals { CmpOp::Eq } else { CmpOp::Ne },
775                    actual,
776                    expected,
777                ))
778            }
779            Self::MemberEquals {
780                member,
781                value,
782                equals,
783            } => {
784                let actual = ir.expr(PExpr::Member(member));
785                let expected = ir.expr(PExpr::Int(value));
786                ir.expr(PExpr::Cmp(
787                    if equals { CmpOp::Eq } else { CmpOp::Ne },
788                    actual,
789                    expected,
790                ))
791            }
792        }
793    }
794
795    #[must_use]
796    pub const fn failure_message(self) -> Option<&'static str> {
797        match self {
798            Self::FalseWithMessage { message } => Some(message),
799            Self::True
800            | Self::False
801            | Self::Invoke { .. }
802            | Self::LookaheadTextEquals { .. }
803            | Self::LookaheadNotEquals { .. }
804            | Self::TokenPairAdjacent
805            | Self::ContextChildRuleTextNotEquals { .. }
806            | Self::LocalIntEquals { .. }
807            | Self::LocalIntLessOrEqual { .. }
808            | Self::MemberModuloEquals { .. }
809            | Self::MemberEquals { .. } => None,
810        }
811    }
812}
813
814fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
815    let local = ir.expr(PExpr::LocalArg);
816    let absent = ir.expr(PExpr::IsNull(local));
817    let expected = ir.expr(PExpr::Int(value));
818    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
819    ir.expr(PExpr::Or([absent, comparison].into()))
820}
821
822/// Policy for semantic predicate coordinates that have no runtime
823/// implementation.
824///
825/// ANTLR grammars may embed target-language predicates that the metadata
826/// generator could not translate into a [`ParserPredicate`] table entry. When
827/// recognition reaches such a coordinate the runtime cannot know the grammar
828/// author's intent, so the caller chooses how to proceed.
829///
830/// The default is [`Self::AssumeTrue`], matching the historical behavior of
831/// this runtime. That default is deprecated and will change to [`Self::Error`]
832/// in a future minor release; grammars relying on unconditional predicates
833/// should opt in explicitly.
834#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
835pub enum UnknownSemanticPolicy {
836    /// Treat the predicate as passing, as if it were absent from the grammar.
837    #[default]
838    AssumeTrue,
839    /// Treat the predicate as failing, removing the guarded alternative.
840    AssumeFalse,
841    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
842    /// coordinate that recognition evaluated.
843    Error,
844}
845
846/// Resolves a predicate coordinate that neither a translated table entry nor a
847/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
848///
849/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
850/// so the parse entry can surface every unresolved coordinate afterwards. Both
851/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
852/// funnel through here so a missing implementation is never silently coerced
853/// to a boolean (design goal G1: never silently mis-parse).
854fn apply_unknown_predicate_policy(
855    policy: UnknownSemanticPolicy,
856    rule_index: usize,
857    pred_index: usize,
858    hits: &mut Vec<(usize, usize)>,
859) -> bool {
860    match policy {
861        UnknownSemanticPolicy::AssumeTrue => true,
862        UnknownSemanticPolicy::AssumeFalse => false,
863        UnknownSemanticPolicy::Error => {
864            let coordinate = (rule_index, pred_index);
865            if !hits.contains(&coordinate) {
866                hits.push(coordinate);
867            }
868            false
869        }
870    }
871}
872
873/// Interval-set of expected token types, displayable through a vocabulary —
874/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
875/// generated test actions.
876#[derive(Clone, Debug, Eq, PartialEq)]
877pub struct ExpectedTokenSet {
878    symbols: BTreeSet<i32>,
879}
880
881impl ExpectedTokenSet {
882    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
883    #[must_use]
884    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
885        expected_symbols_display(&self.symbols, vocabulary)
886    }
887}
888
889/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
890///
891/// The first syntax error aborts the parse instead of recovering. Generated
892/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
893#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
894pub struct BailErrorStrategy;
895
896impl BailErrorStrategy {
897    #[must_use]
898    pub const fn new() -> Self {
899        Self
900    }
901}
902
903/// Prediction strategy requested by generated parser harnesses.
904#[derive(Clone, Copy, Debug, Eq, PartialEq)]
905pub enum PredictionMode {
906    /// Prefer the clean full-context outcome when alternatives reach the same
907    /// input position.
908    Ll,
909    /// Preserve SLL's first-viable alternative bias at a decision, even when a
910    /// later full-context alternative could avoid recovery.
911    Sll,
912    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
913    LlExactAmbigDetection,
914}
915
916/// Integer argument metadata for a generated parser rule invocation.
917///
918/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
919/// the generator records the rule-transition source state and the value that
920/// should be visible to semantic predicates inside the callee.
921#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
922pub struct ParserRuleArg {
923    /// ATN state containing the rule transition that receives this argument.
924    pub source_state: usize,
925    /// Callee rule index for the transition.
926    pub rule_index: usize,
927    /// Literal fallback value to expose in the callee.
928    pub value: i64,
929    /// Whether the callee should inherit the caller's current integer argument.
930    pub inherit_local: bool,
931}
932
933/// Integer member mutation attached to an ATN action transition.
934#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
935pub struct ParserMemberAction {
936    /// ATN state containing the action transition.
937    pub source_state: usize,
938    /// Generator-assigned integer member id.
939    pub member: usize,
940    /// Delta applied when the action is reached on one speculative path.
941    pub delta: i64,
942}
943
944/// Integer return-value assignment attached to an ATN action transition.
945///
946/// Generated parsers use this metadata when target actions assign a simple
947/// return field such as `$y=1000;`. The interpreter applies it while selecting
948/// the recognized path so the finished parse tree can answer later
949/// `$label.y` action templates.
950#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
951pub struct ParserReturnAction {
952    /// ATN state containing the action transition.
953    pub source_state: usize,
954    /// Rule index recorded by the serialized action transition.
955    pub rule_index: usize,
956    /// Return-field name as it appears in the grammar.
957    pub name: &'static str,
958    /// Literal integer value assigned by the action.
959    pub value: i64,
960}
961
962impl ParserMemberAction {
963    /// Lowers this speculative member mutation into a `SemIR` action.
964    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
965        let delta = ir.expr(PExpr::Int(self.delta));
966        ParserSemanticAction {
967            source_state: self.source_state,
968            rule_index: usize::MAX,
969            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
970            speculative: true,
971        }
972    }
973}
974
975impl ParserReturnAction {
976    /// Lowers this committed return-value assignment into a `SemIR` action.
977    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
978        let name = ir.intern(self.name);
979        let value = ir.expr(PExpr::Int(self.value));
980        ParserSemanticAction {
981            source_state: self.source_state,
982            rule_index: self.rule_index,
983            stmt: ir.stmt(AStmt::SetReturn(name, value)),
984            speculative: false,
985        }
986    }
987}
988
989/// Parser predicate coordinate lowered into [`SemIr`].
990#[derive(Clone, Copy, Debug, Eq, PartialEq)]
991pub struct ParserSemanticPredicate {
992    /// Serialized rule index that owns this predicate.
993    pub rule_index: usize,
994    /// Predicate index inside the owning rule.
995    pub pred_index: usize,
996    /// Root expression in the associated [`ParserSemantics::ir`] arena.
997    pub expr: ExprId,
998    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
999    pub failure_message: Option<&'static str>,
1000}
1001
1002/// Parser action coordinate lowered into [`SemIr`].
1003#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1004pub struct ParserSemanticAction {
1005    /// ATN state containing the action transition.
1006    pub source_state: usize,
1007    /// Serialized rule index recorded by the action transition.
1008    pub rule_index: usize,
1009    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1010    pub stmt: StmtId,
1011    /// Whether this action may run on speculative recognition paths.
1012    pub speculative: bool,
1013}
1014
1015/// Data-driven semantic tables emitted by generated parsers.
1016///
1017/// This is the runtime representation for issue #9's `SemIR` path. Existing
1018/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1019/// remain accepted as deprecated adapters for generated code produced before
1020/// this table existed.
1021#[derive(Clone, Debug, Default, Eq, PartialEq)]
1022pub struct ParserSemantics {
1023    pub ir: SemIr,
1024    pub predicates: Vec<ParserSemanticPredicate>,
1025    pub actions: Vec<ParserSemanticAction>,
1026}
1027
1028/// Optional generated-runtime metadata for metadata-driven parser execution.
1029#[derive(Clone, Copy, Debug, Default)]
1030pub struct ParserRuntimeOptions<'a> {
1031    /// Rule indexes whose `@init` actions should be replayed.
1032    pub init_action_rules: &'a [usize],
1033    /// Whether generated parse-tree contexts should retain alternative numbers.
1034    pub track_alt_numbers: bool,
1035    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1036    pub predicates: &'a [(usize, usize, ParserPredicate)],
1037    /// `SemIR` predicate/action table emitted by newer generated parsers.
1038    pub semantics: Option<&'a ParserSemantics>,
1039    /// Rule-call integer argument table keyed by ATN source state.
1040    pub rule_args: &'a [ParserRuleArg],
1041    /// Integer member mutations keyed by ATN action source state.
1042    pub member_actions: &'a [ParserMemberAction],
1043    /// Integer return assignments keyed by ATN action source state.
1044    pub return_actions: &'a [ParserReturnAction],
1045    /// How to evaluate semantic predicate coordinates absent from
1046    /// `predicates`.
1047    pub unknown_predicate_policy: UnknownSemanticPolicy,
1048}
1049
1050pub trait Parser: Recognizer {
1051    /// Reports whether generated parser rules should build parse-tree nodes
1052    /// while recognizing input.
1053    fn build_parse_trees(&self) -> bool;
1054
1055    /// Enables or disables parse-tree construction for subsequent rule calls.
1056    fn set_build_parse_trees(&mut self, build: bool);
1057
1058    /// Returns the number of parser syntax errors recorded by committed parse
1059    /// paths so far.
1060    fn number_of_syntax_errors(&self) -> usize {
1061        0
1062    }
1063
1064    /// Reports whether prediction diagnostic-listener messages are emitted
1065    /// during parser ATN recognition.
1066    fn report_diagnostic_errors(&self) -> bool {
1067        false
1068    }
1069
1070    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1071    /// rule calls.
1072    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1073
1074    /// Reports the prediction strategy used when selecting among alternatives.
1075    fn prediction_mode(&self) -> PredictionMode {
1076        PredictionMode::Ll
1077    }
1078
1079    /// Sets the prediction strategy for subsequent rule calls.
1080    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1081}
1082
1083#[derive(Debug)]
1084struct LeftRecursiveCallerOverlap {
1085    atn_key: SharedAtnCacheKey,
1086    state_number: usize,
1087    symbol: i32,
1088    context_version: usize,
1089    overlaps: bool,
1090}
1091
1092const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1093
1094#[derive(Debug)]
1095pub struct BaseParser<S, H = NoSemanticHooks> {
1096    input: CommonTokenStream<S>,
1097    tree: ParseTreeStorage,
1098    data: RecognizerData,
1099    semantic_hooks: H,
1100    build_parse_trees: bool,
1101    syntax_errors: usize,
1102    report_diagnostic_errors: bool,
1103    prediction_mode: PredictionMode,
1104    prediction_diagnostics: Vec<ParserDiagnostic>,
1105    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1106    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1107    generated_sync_expected: Option<TokenBitSet>,
1108    int_members: BTreeMap<usize, i64>,
1109    rule_context_stack: Vec<RuleContextFrame>,
1110    rule_context_version: usize,
1111    left_recursive_caller_overlap_cache:
1112        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1113    pending_invoking_states: Vec<isize>,
1114    precedence_stack: Vec<i32>,
1115    /// Predicate side effects are observable in a few target-template tests;
1116    /// speculative recognition may revisit the same coordinate, so replay it
1117    /// once per parser instance.
1118    invoked_predicates: Vec<(usize, usize)>,
1119    /// Bail error strategy: the first syntax error aborts the parse instead of
1120    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1121    /// through `set_error_handler(BailErrorStrategy::new())`.
1122    bail_on_error: bool,
1123    /// How to evaluate predicate coordinates missing from the active
1124    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1125    unknown_predicate_policy: UnknownSemanticPolicy,
1126    /// Unknown predicate coordinates evaluated by the current parse, recorded
1127    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1128    unknown_predicate_hits: Vec<(usize, usize)>,
1129    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1130    /// that no hook handled, recorded so a generated `hook`/error-disposed
1131    /// action fails loud instead of being silently dropped. Keyed by
1132    /// `(rule_index, source_state)`.
1133    unhandled_action_hits: Vec<(usize, usize)>,
1134    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1135    /// hot rule-transition checks to a vector lookup after the first visit
1136    /// while the thread-local shared ATN cache still owns the cross-parse
1137    /// computed value.
1138    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1139    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1140    /// epsilon-reachable consuming transition and shows up as a hot loop in
1141    /// `next_recovery_context` and recovery diagnostics on long inputs.
1142    /// Keying on `state_number` and sharing the result through `Rc` removes
1143    /// repeated DFS plus per-call `BTreeSet` allocations.
1144    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1145    /// Same expected-symbol cache as a bitset for generated parser sync.
1146    /// Successful parses only need `contains` and union; keeping that path out
1147    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1148    /// check and defers deterministic formatting to diagnostics.
1149    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1150    /// Per-state cache for whether a return state can finish its owning rule
1151    /// without consuming more input. Generated-parser sync uses this to walk
1152    /// parent prediction contexts for nullable exits without paying repeated
1153    /// epsilon-closure searches on every loop or optional decision.
1154    rule_stop_reach_cache: Vec<Option<bool>>,
1155    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1156    /// threads the same epsilon-recovery context through hundreds of follow
1157    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1158    /// reference bump and lets the memo key hash by pointer.
1159    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1160    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1161    /// touch a given decision state still pay no upfront cost; once cached,
1162    /// the recognizer prunes alternatives whose look-1 cannot accept the
1163    /// current lookahead, letting common SLL decisions reduce to a single
1164    /// transition walk instead of a full speculative fan-out.
1165    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1166    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1167    /// Each multi-trans visit asks "given this decision state and this
1168    /// lookahead token, which alt do I commit to?" Hitting this cache
1169    /// turns the question into a hashmap probe instead of re-scanning
1170    /// the decision's per-transition FIRST sets every visit.
1171    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1172    /// Predicate results shared by the fast recognizer's clean and recovery
1173    /// attempts. The eligible fast path keeps every runtime-provided input
1174    /// fixed, and custom predicate hooks are required to be replay-safe.
1175    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1176    /// Per-parse cache for whether an ATN state can reach itself without
1177    /// consuming input. Only those states need the recursive recognizer's
1178    /// `(state, token-index)` cycle guard.
1179    empty_cycle_cache: Vec<Option<bool>>,
1180    /// Probe state for deciding whether clean-pass one-outcome memo entries
1181    /// are worth storing for the current parse.
1182    single_outcome_memo_mode: SingleOutcomeMemoMode,
1183    single_outcome_probe_seen: FxHashSet<FastRecognizeKey>,
1184    single_outcome_probe_samples: usize,
1185    single_outcome_probe_repeats: usize,
1186    /// Reusable direct-index/hash storage for clean speculative endpoints.
1187    fast_outcome_dedup: FastOutcomeDedupScratch,
1188    /// Empty recovery-symbols singleton used as the default at rule entry and
1189    /// after token consumption.
1190    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1191    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1192    /// prefilter trims speculative rule calls whose called rule cannot
1193    /// match the current lookahead, but it also bypasses single-token
1194    /// insertion / deletion recovery that ANTLR runs at the rule's first
1195    /// consuming transition. `parse_atn_rule` flips this off and retries
1196    /// when the first pass produces no clean outcome so the runtime can
1197    /// repair inputs the reference parser would have repaired.
1198    fast_first_set_prefilter: bool,
1199    /// Whether the fast recognizer should explore parser error-recovery paths.
1200    /// Public rule parsing starts with this disabled for the common valid-input
1201    /// path and enables it only for the retry that needs ANTLR-style repairs.
1202    fast_recovery_enabled: bool,
1203    /// Whether the fast recognizer should record terminal-token nodes while
1204    /// speculating. Clean valid-input parsing can reconstruct terminals from
1205    /// selected rule spans after recognition, avoiding many speculative
1206    /// nodes that are thrown away with losing paths.
1207    fast_token_nodes_enabled: bool,
1208    /// Parser-owned append-only storage for speculative recognition output.
1209    /// Each public interpreted-rule entry clears lengths while retaining
1210    /// bounded backing capacities for parser reuse.
1211    recognition_arena: RecognitionArena,
1212    last_recognition_arena_root: NodeSeqId,
1213    last_recognition_arena_diagnostics: DiagnosticSeqId,
1214}
1215
1216/// Rollback marker for speculative generated parser paths.
1217#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1218pub struct GeneratedDiagnosticsCheckpoint {
1219    diagnostics_len: usize,
1220    syntax_errors: usize,
1221    tree: ParseTreeCheckpoint,
1222}
1223
1224/// Storage and reachability counters for the most recent interpreted-rule
1225/// recognition arena.
1226#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1227pub struct RecognitionArenaStats {
1228    pub total_nodes: usize,
1229    pub live_nodes: usize,
1230    pub dead_nodes: usize,
1231    pub node_capacity: usize,
1232    pub total_links: usize,
1233    pub live_links: usize,
1234    pub dead_links: usize,
1235    pub link_capacity: usize,
1236    pub total_extras: usize,
1237    pub live_extras: usize,
1238    pub dead_extras: usize,
1239    pub extra_capacity: usize,
1240}
1241
1242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1243struct RuleContextFrame {
1244    rule_index: usize,
1245    invoking_state: isize,
1246}
1247
1248#[derive(Clone, Debug, Eq, PartialEq)]
1249struct RecognizeOutcome {
1250    index: usize,
1251    consumed_eof: bool,
1252    alt_number: usize,
1253    member_values: BTreeMap<usize, i64>,
1254    return_values: BTreeMap<String, i64>,
1255    diagnostics: DiagnosticSeqId,
1256    decisions: Vec<usize>,
1257    actions: Vec<ParserAction>,
1258    nodes: NodeSeqId,
1259}
1260
1261#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1262struct FastRecognizeOutcome {
1263    index: usize,
1264    consumed_eof: bool,
1265    diagnostics: DiagnosticSeqId,
1266    deferred_nodes: FastDeferredNodeId,
1267    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1268    /// Copying an outcome copies this compact ID; prepending appends one
1269    /// `SeqLink` without allocating an individual node or list tail.
1270    nodes: NodeSeqId,
1271}
1272
1273#[derive(Debug, Default)]
1274struct FastOutcomeDedupScratch {
1275    dense_words: Vec<u64>,
1276    touched_dense_words: Vec<u32>,
1277    sparse_keys: FxHashSet<(usize, bool)>,
1278}
1279
1280/// Handle into the parser-owned deferred tree rope.
1281///
1282/// The sentinel keeps outcomes and repetition paths compact without an
1283/// `Option` discriminant or per-node reference counting.
1284#[repr(transparent)]
1285#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1286struct FastDeferredNodeId(u32);
1287
1288impl FastDeferredNodeId {
1289    const EMPTY: Self = Self(u32::MAX);
1290
1291    const fn is_empty(self) -> bool {
1292        self.0 == Self::EMPTY.0
1293    }
1294}
1295
1296impl Default for FastDeferredNodeId {
1297    fn default() -> Self {
1298        Self::EMPTY
1299    }
1300}
1301
1302#[repr(transparent)]
1303#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1304struct FastDeferredRuleId(u32);
1305
1306/// One immutable deferred-tree rope record in `RecognitionArena`.
1307#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1308enum FastDeferredNode {
1309    Fragment(NodeSeqId),
1310    Rule(FastDeferredRuleId),
1311    Concat {
1312        prefix: FastDeferredNodeId,
1313        suffix: FastDeferredNodeId,
1314    },
1315}
1316
1317#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1318struct FastDeferredRule {
1319    rule_index: u32,
1320    invoking_state: i32,
1321    start_index: u32,
1322    stop_index: Option<u32>,
1323    deferred_children: FastDeferredNodeId,
1324    children: NodeSeqId,
1325}
1326
1327#[repr(transparent)]
1328#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1329struct RecognizedNodeId(u32);
1330
1331#[repr(transparent)]
1332#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1333struct NodeSeqId(u32);
1334
1335impl NodeSeqId {
1336    const EMPTY: Self = Self(u32::MAX);
1337
1338    const fn is_empty(self) -> bool {
1339        self.0 == Self::EMPTY.0
1340    }
1341}
1342
1343impl Default for NodeSeqId {
1344    fn default() -> Self {
1345        Self::EMPTY
1346    }
1347}
1348
1349#[repr(transparent)]
1350#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1351struct DiagnosticSeqId(u32);
1352
1353impl DiagnosticSeqId {
1354    const EMPTY: Self = Self(u32::MAX);
1355
1356    const fn is_empty(self) -> bool {
1357        self.0 == Self::EMPTY.0
1358    }
1359}
1360
1361impl Default for DiagnosticSeqId {
1362    fn default() -> Self {
1363        Self::EMPTY
1364    }
1365}
1366
1367#[repr(transparent)]
1368#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1369struct RecognitionExtraId(u32);
1370
1371#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1372struct SeqLink {
1373    head: RecognizedNodeId,
1374    tail: NodeSeqId,
1375}
1376
1377#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1378struct DiagnosticLink {
1379    head: RecognitionExtraId,
1380    tail: DiagnosticSeqId,
1381}
1382
1383struct ArenaRuleSpec {
1384    rule_index: usize,
1385    invoking_state: isize,
1386    alt_number: usize,
1387    start_index: usize,
1388    stop_index: Option<usize>,
1389    return_values: BTreeMap<String, i64>,
1390    children: NodeSeqId,
1391}
1392
1393/// Compact speculative node record. Common records contain only IDs and
1394/// scalars; missing-token text and generated return values live in `extras`.
1395#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1396enum ArenaRecognizedNode {
1397    Token {
1398        token: TokenId,
1399    },
1400    ErrorToken {
1401        token: TokenId,
1402    },
1403    MissingToken {
1404        extra: RecognitionExtraId,
1405    },
1406    Rule {
1407        rule_index: u32,
1408        invoking_state: i32,
1409        alt_number: u32,
1410        start_index: u32,
1411        stop_index: Option<u32>,
1412        return_values: Option<RecognitionExtraId>,
1413        children: NodeSeqId,
1414    },
1415    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1416    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1417    /// public rule entry hands the tree to the caller.
1418    LeftRecursiveBoundary {
1419        rule_index: u32,
1420    },
1421}
1422
1423#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1424enum RecognitionExtra {
1425    MissingToken {
1426        token_type: i32,
1427        at_index: u32,
1428        text: String,
1429    },
1430    ReturnValues(BTreeMap<String, i64>),
1431    Diagnostic(ParserDiagnostic),
1432}
1433
1434#[derive(Debug, Default)]
1435struct RecognitionArena {
1436    nodes: Vec<ArenaRecognizedNode>,
1437    seq_links: Vec<SeqLink>,
1438    diagnostic_links: Vec<DiagnosticLink>,
1439    extras: Vec<RecognitionExtra>,
1440    deferred_nodes: Vec<FastDeferredNode>,
1441    deferred_rules: Vec<FastDeferredRule>,
1442}
1443
1444// Preserve normal parser reuse while preventing one pathological parse from
1445// pinning an arbitrarily large arena for the parser's remaining lifetime.
1446const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1447const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1448const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1449const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1450const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1451const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1452
1453impl RecognitionArena {
1454    fn reset(&mut self) {
1455        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1456        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1457        reset_arena_vec(
1458            &mut self.diagnostic_links,
1459            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1460        );
1461        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1462        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1463        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1464    }
1465
1466    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1467        let id = RecognizedNodeId(
1468            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1469        );
1470        self.nodes.push(node);
1471        id
1472    }
1473
1474    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1475        let id = RecognitionExtraId(
1476            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1477        );
1478        self.extras.push(extra);
1479        id
1480    }
1481
1482    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1483        let id = NodeSeqId(
1484            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1485        );
1486        self.seq_links.push(SeqLink { head, tail });
1487        id
1488    }
1489
1490    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1491        let id = FastDeferredNodeId(
1492            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1493        );
1494        self.deferred_nodes.push(node);
1495        id
1496    }
1497
1498    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1499        let id = FastDeferredRuleId(
1500            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1501        );
1502        self.deferred_rules.push(rule);
1503        id
1504    }
1505
1506    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1507        if nodes.is_empty() {
1508            FastDeferredNodeId::EMPTY
1509        } else {
1510            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1511        }
1512    }
1513
1514    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1515        let rule = self.push_deferred_rule(rule);
1516        self.push_deferred_node(FastDeferredNode::Rule(rule))
1517    }
1518
1519    fn concat_deferred_nodes(
1520        &mut self,
1521        prefix: FastDeferredNodeId,
1522        suffix: FastDeferredNodeId,
1523    ) -> FastDeferredNodeId {
1524        if prefix.is_empty() {
1525            return suffix;
1526        }
1527        if suffix.is_empty() {
1528            return prefix;
1529        }
1530        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1531    }
1532
1533    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1534        self.deferred_nodes[id.0 as usize]
1535    }
1536
1537    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1538        self.deferred_rules[id.0 as usize]
1539    }
1540
1541    fn prepend_diagnostic(
1542        &mut self,
1543        tail: DiagnosticSeqId,
1544        diagnostic: ParserDiagnostic,
1545    ) -> DiagnosticSeqId {
1546        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1547        self.prepend_diagnostic_id(tail, head)
1548    }
1549
1550    fn prepend_diagnostic_id(
1551        &mut self,
1552        tail: DiagnosticSeqId,
1553        head: RecognitionExtraId,
1554    ) -> DiagnosticSeqId {
1555        let id = DiagnosticSeqId(
1556            u32::try_from(self.diagnostic_links.len())
1557                .expect("diagnostic sequence arena fits in u32"),
1558        );
1559        self.diagnostic_links.push(DiagnosticLink { head, tail });
1560        id
1561    }
1562
1563    fn concat_diagnostics(
1564        &mut self,
1565        prefix: DiagnosticSeqId,
1566        mut suffix: DiagnosticSeqId,
1567    ) -> DiagnosticSeqId {
1568        if prefix.is_empty() {
1569            return suffix;
1570        }
1571        if suffix.is_empty() {
1572            return prefix;
1573        }
1574        let mut reversed = DiagnosticSeqId::EMPTY;
1575        let mut cursor = prefix;
1576        while let Some(link) = self.diagnostic_link(cursor) {
1577            reversed = self.prepend_diagnostic_id(reversed, link.head);
1578            cursor = link.tail;
1579        }
1580        while let Some(link) = self.diagnostic_link(reversed) {
1581            suffix = self.prepend_diagnostic_id(suffix, link.head);
1582            reversed = link.tail;
1583        }
1584        suffix
1585    }
1586
1587    #[cfg(test)]
1588    fn diagnostic_sequence(
1589        &mut self,
1590        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1591    ) -> DiagnosticSeqId {
1592        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1593        let mut sequence = DiagnosticSeqId::EMPTY;
1594        for diagnostic in diagnostics.into_iter().rev() {
1595            sequence = self.prepend_diagnostic(sequence, diagnostic);
1596        }
1597        sequence
1598    }
1599
1600    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1601        self.nodes[id.0 as usize]
1602    }
1603
1604    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1605        &self.extras[id.0 as usize]
1606    }
1607
1608    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1609        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1610    }
1611
1612    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1613        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1614    }
1615
1616    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1617        NodeSeqIter {
1618            arena: self,
1619            cursor: sequence,
1620        }
1621    }
1622
1623    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1624        DiagnosticSeqIter {
1625            arena: self,
1626            cursor: sequence,
1627        }
1628    }
1629
1630    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1631        self.diagnostics(sequence).count()
1632    }
1633
1634    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1635        self.diagnostics(sequence)
1636            .filter(|diagnostic| {
1637                diagnostic.message.starts_with("mismatched input ")
1638                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1639            })
1640            .count()
1641    }
1642
1643    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1644        self.diagnostics(left).cmp(self.diagnostics(right))
1645    }
1646
1647    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1648        self.iter(sequence).count()
1649    }
1650
1651    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1652        self.iter(sequence).any(|node| match self.node(node) {
1653            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1654            ArenaRecognizedNode::Rule { children, .. } => {
1655                self.sequence_has_left_recursive_boundary(children)
1656            }
1657            ArenaRecognizedNode::Token { .. }
1658            | ArenaRecognizedNode::ErrorToken { .. }
1659            | ArenaRecognizedNode::MissingToken { .. } => false,
1660        })
1661    }
1662
1663    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1664        self.iter(sequence).any(|node| {
1665            matches!(
1666                self.node(node),
1667                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1668            )
1669        })
1670    }
1671
1672    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1673        self.iter(sequence).any(|node| {
1674            matches!(
1675                self.node(node),
1676                ArenaRecognizedNode::Token { .. }
1677                    | ArenaRecognizedNode::ErrorToken { .. }
1678                    | ArenaRecognizedNode::MissingToken { .. }
1679            )
1680        })
1681    }
1682
1683    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1684        match self.node(node) {
1685            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1686                Some(token.index())
1687            }
1688            ArenaRecognizedNode::MissingToken { extra } => {
1689                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1690                    unreachable!("missing-token node must reference missing-token extra");
1691                };
1692                Some(*at_index as usize)
1693            }
1694            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1695            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1696        }
1697    }
1698
1699    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
1700        match self.node(node) {
1701            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1702                Some(token.index())
1703            }
1704            ArenaRecognizedNode::MissingToken { extra } => {
1705                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1706                    unreachable!("missing-token node must reference missing-token extra");
1707                };
1708                (*at_index as usize).checked_sub(1)
1709            }
1710            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
1711            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1712        }
1713    }
1714
1715    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
1716        let start = self.node_start_index(node)?;
1717        let stop = self.node_stop_index(node);
1718        Some((start, stop))
1719    }
1720
1721    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
1722        self.iter(sequence)
1723            .find_map(|node| self.node_start_index(node))
1724    }
1725
1726    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
1727        let mut stop = None;
1728        for node in self.iter(sequence) {
1729            if let Some(index) = self.node_stop_index(node) {
1730                stop = Some(index);
1731            }
1732        }
1733        stop
1734    }
1735
1736    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
1737        self.iter(sequence)
1738            .any(|node| self.node_needs_stable_tie(node))
1739    }
1740
1741    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
1742        match self.node(node) {
1743            ArenaRecognizedNode::Token { .. }
1744            | ArenaRecognizedNode::ErrorToken { .. }
1745            | ArenaRecognizedNode::MissingToken { .. } => false,
1746            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1747            ArenaRecognizedNode::Rule {
1748                rule_index,
1749                children,
1750                ..
1751            } => self.iter(children).any(|child| {
1752                matches!(
1753                    self.node(child),
1754                    ArenaRecognizedNode::Rule {
1755                        rule_index: child_rule,
1756                        ..
1757                    } if child_rule == rule_index
1758                ) || self.node_needs_stable_tie(child)
1759            }),
1760        }
1761    }
1762
1763    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
1764        loop {
1765            match (self.link(left), self.link(right)) {
1766                (Some(left_link), Some(right_link)) => {
1767                    let order = self.compare_nodes(left_link.head, right_link.head);
1768                    if order != Ordering::Equal {
1769                        return order;
1770                    }
1771                    left = left_link.tail;
1772                    right = right_link.tail;
1773                }
1774                (None, None) => return Ordering::Equal,
1775                (None, Some(_)) => return Ordering::Less,
1776                (Some(_), None) => return Ordering::Greater,
1777            }
1778        }
1779    }
1780
1781    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
1782        let left = self.node(left);
1783        let right = self.node(right);
1784        match (left, right) {
1785            (
1786                ArenaRecognizedNode::Token { token: left },
1787                ArenaRecognizedNode::Token { token: right },
1788            )
1789            | (
1790                ArenaRecognizedNode::ErrorToken { token: left },
1791                ArenaRecognizedNode::ErrorToken { token: right },
1792            ) => left.cmp(&right),
1793            (
1794                ArenaRecognizedNode::MissingToken { extra: left },
1795                ArenaRecognizedNode::MissingToken { extra: right },
1796            ) => self.extra(left).cmp(self.extra(right)),
1797            (
1798                ArenaRecognizedNode::Rule {
1799                    rule_index: left_rule,
1800                    invoking_state: left_invoking,
1801                    alt_number: left_alt,
1802                    start_index: left_start,
1803                    stop_index: left_stop,
1804                    return_values: left_returns,
1805                    children: left_children,
1806                },
1807                ArenaRecognizedNode::Rule {
1808                    rule_index: right_rule,
1809                    invoking_state: right_invoking,
1810                    alt_number: right_alt,
1811                    start_index: right_start,
1812                    stop_index: right_stop,
1813                    return_values: right_returns,
1814                    children: right_children,
1815                },
1816            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
1817                .cmp(&(
1818                    right_rule,
1819                    right_invoking,
1820                    right_alt,
1821                    right_start,
1822                    right_stop,
1823                ))
1824                .then_with(|| {
1825                    left_returns
1826                        .map(|id| self.extra(id))
1827                        .cmp(&right_returns.map(|id| self.extra(id)))
1828                })
1829                .then_with(|| self.compare_sequences(left_children, right_children)),
1830            (
1831                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: left },
1832                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: right },
1833            ) => left.cmp(&right),
1834            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
1835        }
1836    }
1837
1838    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1839        let mut reversed = NodeSeqId::EMPTY;
1840        while let Some(link) = self.link(sequence) {
1841            reversed = self.prepend(reversed, link.head);
1842            sequence = link.tail;
1843        }
1844        reversed
1845    }
1846
1847    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1848        if !self.sequence_has_direct_boundary(sequence) {
1849            return sequence;
1850        }
1851        let mut reversed = NodeSeqId::EMPTY;
1852        while let Some(link) = self.link(sequence) {
1853            match self.node(link.head) {
1854                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
1855                    if !reversed.is_empty() {
1856                        let children = self.reverse_sequence(reversed);
1857                        let start_index = self.sequence_start_index(children).unwrap_or_default();
1858                        let stop_index = self.sequence_stop_index(children);
1859                        let rule = self.push_node(ArenaRecognizedNode::Rule {
1860                            rule_index,
1861                            invoking_state: -1,
1862                            alt_number: 0,
1863                            start_index: u32::try_from(start_index)
1864                                .expect("left-recursive start index fits in u32"),
1865                            stop_index: stop_index.map(|index| {
1866                                u32::try_from(index).expect("left-recursive stop index fits in u32")
1867                            }),
1868                            return_values: None,
1869                            children,
1870                        });
1871                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
1872                    }
1873                }
1874                _ => {
1875                    reversed = self.prepend(reversed, link.head);
1876                }
1877            }
1878            sequence = link.tail;
1879        }
1880        self.reverse_sequence(reversed)
1881    }
1882
1883    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
1884        let mut live_nodes = vec![false; self.nodes.len()];
1885        let mut live_links = vec![false; self.seq_links.len()];
1886        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
1887        let mut live_extras = vec![false; self.extras.len()];
1888        let mut pending = vec![root];
1889        while let Some(mut sequence) = pending.pop() {
1890            while let Some(link) = self.link(sequence) {
1891                let link_index = sequence.0 as usize;
1892                if live_links[link_index] {
1893                    break;
1894                }
1895                live_links[link_index] = true;
1896                let node_index = link.head.0 as usize;
1897                if !live_nodes[node_index] {
1898                    live_nodes[node_index] = true;
1899                    match self.node(link.head) {
1900                        ArenaRecognizedNode::MissingToken { extra } => {
1901                            live_extras[extra.0 as usize] = true;
1902                        }
1903                        ArenaRecognizedNode::Rule {
1904                            return_values,
1905                            children,
1906                            ..
1907                        } => {
1908                            if let Some(extra) = return_values {
1909                                live_extras[extra.0 as usize] = true;
1910                            }
1911                            pending.push(children);
1912                        }
1913                        ArenaRecognizedNode::Token { .. }
1914                        | ArenaRecognizedNode::ErrorToken { .. }
1915                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
1916                    }
1917                }
1918                sequence = link.tail;
1919            }
1920        }
1921        let mut diagnostics = diagnostics;
1922        while let Some(link) = self.diagnostic_link(diagnostics) {
1923            let link_index = diagnostics.0 as usize;
1924            if live_diagnostic_links[link_index] {
1925                break;
1926            }
1927            live_diagnostic_links[link_index] = true;
1928            live_extras[link.head.0 as usize] = true;
1929            diagnostics = link.tail;
1930        }
1931        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
1932        let live_link_count = live_links.into_iter().filter(|live| *live).count()
1933            + live_diagnostic_links
1934                .into_iter()
1935                .filter(|live| *live)
1936                .count();
1937        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
1938        let total_links = self.seq_links.len() + self.diagnostic_links.len();
1939        RecognitionArenaStats {
1940            total_nodes: self.nodes.len(),
1941            live_nodes: live_node_count,
1942            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
1943            node_capacity: self.nodes.capacity(),
1944            total_links,
1945            live_links: live_link_count,
1946            dead_links: total_links.saturating_sub(live_link_count),
1947            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
1948            total_extras: self.extras.len(),
1949            live_extras: live_extra_count,
1950            dead_extras: self.extras.len().saturating_sub(live_extra_count),
1951            extra_capacity: self.extras.capacity(),
1952        }
1953    }
1954}
1955
1956fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
1957    if storage.capacity() > max_retained_capacity {
1958        *storage = Vec::new();
1959    } else {
1960        storage.clear();
1961    }
1962}
1963
1964const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
1965    match node {
1966        ArenaRecognizedNode::Token { .. } => 0,
1967        ArenaRecognizedNode::ErrorToken { .. } => 1,
1968        ArenaRecognizedNode::MissingToken { .. } => 2,
1969        ArenaRecognizedNode::Rule { .. } => 3,
1970        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
1971    }
1972}
1973
1974struct NodeSeqIter<'a> {
1975    arena: &'a RecognitionArena,
1976    cursor: NodeSeqId,
1977}
1978
1979impl Iterator for NodeSeqIter<'_> {
1980    type Item = RecognizedNodeId;
1981
1982    fn next(&mut self) -> Option<Self::Item> {
1983        let link = self.arena.link(self.cursor)?;
1984        self.cursor = link.tail;
1985        Some(link.head)
1986    }
1987}
1988
1989struct DiagnosticSeqIter<'a> {
1990    arena: &'a RecognitionArena,
1991    cursor: DiagnosticSeqId,
1992}
1993
1994impl<'a> Iterator for DiagnosticSeqIter<'a> {
1995    type Item = &'a ParserDiagnostic;
1996
1997    fn next(&mut self) -> Option<Self::Item> {
1998        let link = self.arena.diagnostic_link(self.cursor)?;
1999        self.cursor = link.tail;
2000        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2001            unreachable!("diagnostic link must reference diagnostic extra");
2002        };
2003        Some(diagnostic)
2004    }
2005}
2006
2007#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2008struct ParserDiagnostic {
2009    line: usize,
2010    column: usize,
2011    message: String,
2012}
2013
2014#[derive(Clone, Debug, Default, Eq, PartialEq)]
2015struct ExpectedTokens {
2016    index: Option<usize>,
2017    symbols: BTreeSet<i32>,
2018    no_viable: Option<NoViableAlternative>,
2019}
2020
2021#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2022struct NoViableAlternative {
2023    start_index: usize,
2024    error_index: usize,
2025}
2026
2027impl ExpectedTokens {
2028    /// Records the expected symbols for the farthest token index reached by any
2029    /// failed ATN path.
2030    fn record_transition(
2031        &mut self,
2032        index: usize,
2033        transition: ParserTransition<'_>,
2034        max_token_type: i32,
2035    ) {
2036        let symbols = transition_expected_symbols(transition, max_token_type);
2037        match self.index {
2038            Some(current) if index < current => {}
2039            Some(current) if index == current => self.symbols.extend(symbols),
2040            _ => {
2041                self.index = Some(index);
2042                self.symbols = symbols;
2043            }
2044        }
2045    }
2046
2047    /// Records an ambiguous decision that failed after consuming a shared
2048    /// prefix, which ANTLR reports as `no viable alternative`.
2049    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2050        match self.no_viable {
2051            Some(current) if error_index < current.error_index => {}
2052            _ => {
2053                self.no_viable = Some(NoViableAlternative {
2054                    start_index,
2055                    error_index,
2056                });
2057            }
2058        }
2059    }
2060}
2061
2062/// Compact token-type set for parser-internal FIRST/lookahead caches.
2063///
2064/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2065/// but the hot recognizer path mostly needs `contains` and set union over
2066/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2067/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2068#[derive(Clone, Debug, Default, Eq, PartialEq)]
2069struct TokenBitSet {
2070    words: Vec<u64>,
2071}
2072
2073impl TokenBitSet {
2074    fn insert(&mut self, symbol: i32) {
2075        let Some(slot) = token_bit_slot(symbol) else {
2076            return;
2077        };
2078        let word = slot / u64::BITS as usize;
2079        if word >= self.words.len() {
2080            self.words.resize(word + 1, 0);
2081        }
2082        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2083    }
2084
2085    fn extend_range(&mut self, start: i32, stop: i32) {
2086        let (start, stop) = if start <= stop {
2087            (start, stop)
2088        } else {
2089            (stop, start)
2090        };
2091        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2092            self.insert(TOKEN_EOF);
2093        }
2094        let positive_start = start.max(1);
2095        if positive_start > stop {
2096            return;
2097        }
2098        let Some(start_slot) = token_bit_slot(positive_start) else {
2099            return;
2100        };
2101        let Some(stop_slot) = token_bit_slot(stop) else {
2102            return;
2103        };
2104        self.extend_slot_range(start_slot, stop_slot);
2105    }
2106
2107    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2108        if start_slot > stop_slot {
2109            return;
2110        }
2111        let start_word = start_slot / u64::BITS as usize;
2112        let stop_word = stop_slot / u64::BITS as usize;
2113        if stop_word >= self.words.len() {
2114            self.words.resize(stop_word + 1, 0);
2115        }
2116        let start_offset = start_slot % u64::BITS as usize;
2117        let stop_offset = stop_slot % u64::BITS as usize;
2118        if start_word == stop_word {
2119            self.words[start_word] |=
2120                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2121            return;
2122        }
2123        self.words[start_word] |= !0_u64 << start_offset;
2124        for word in &mut self.words[(start_word + 1)..stop_word] {
2125            *word = !0_u64;
2126        }
2127        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2128    }
2129
2130    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2131        for symbol in symbols {
2132            self.insert(symbol);
2133        }
2134    }
2135
2136    fn extend_from(&mut self, other: &Self) {
2137        if other.words.len() > self.words.len() {
2138            self.words.resize(other.words.len(), 0);
2139        }
2140        for (left, right) in self.words.iter_mut().zip(&other.words) {
2141            *left |= *right;
2142        }
2143    }
2144
2145    fn contains(&self, symbol: i32) -> bool {
2146        let Some(slot) = token_bit_slot(symbol) else {
2147            return false;
2148        };
2149        let word = slot / u64::BITS as usize;
2150        self.words
2151            .get(word)
2152            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2153    }
2154
2155    fn is_empty(&self) -> bool {
2156        self.words.iter().all(|word| *word == 0)
2157    }
2158
2159    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2160        for (word_index, word) in self.words.iter().copied().enumerate() {
2161            let mut bits = word;
2162            while bits != 0 {
2163                let bit = bits.trailing_zeros() as usize;
2164                if let Some(symbol) = token_bit_symbol(word_index * u64::BITS as usize + bit) {
2165                    target.insert(symbol);
2166                }
2167                bits &= bits - 1;
2168            }
2169        }
2170    }
2171
2172    fn to_btree_set(&self) -> BTreeSet<i32> {
2173        let mut out = BTreeSet::new();
2174        self.extend_btree_set(&mut out);
2175        out
2176    }
2177}
2178
2179fn token_bit_slot(symbol: i32) -> Option<usize> {
2180    if symbol == TOKEN_EOF {
2181        Some(0)
2182    } else if symbol > 0 {
2183        usize::try_from(symbol).ok()
2184    } else {
2185        None
2186    }
2187}
2188
2189fn token_bit_symbol(slot: usize) -> Option<i32> {
2190    if slot == 0 {
2191        Some(TOKEN_EOF)
2192    } else {
2193        i32::try_from(slot).ok()
2194    }
2195}
2196
2197/// Converts one consuming transition into the token types that would satisfy it
2198/// for diagnostic reporting.
2199fn transition_expected_symbols(
2200    transition: ParserTransition<'_>,
2201    max_token_type: i32,
2202) -> BTreeSet<i32> {
2203    let mut symbols = BTreeSet::new();
2204    match &transition.data() {
2205        Transition::Atom { label, .. } => {
2206            symbols.insert(*label);
2207        }
2208        Transition::Range { start, stop, .. } => {
2209            symbols.extend(*start..=*stop);
2210        }
2211        Transition::Set { set, .. } => {
2212            for (start, stop) in set.ranges() {
2213                symbols.extend(start..=stop);
2214            }
2215        }
2216        Transition::NotSet { set, .. } => {
2217            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2218        }
2219        Transition::Wildcard { .. } => {
2220            symbols.extend(1..=max_token_type);
2221        }
2222        Transition::Epsilon { .. }
2223        | Transition::Rule { .. }
2224        | Transition::Predicate { .. }
2225        | Transition::Action { .. }
2226        | Transition::Precedence { .. } => {}
2227    }
2228    symbols
2229}
2230
2231fn transition_expected_token_set(
2232    transition: ParserTransition<'_>,
2233    max_token_type: i32,
2234) -> TokenBitSet {
2235    let mut symbols = TokenBitSet::default();
2236    match &transition.data() {
2237        Transition::Atom { label, .. } => {
2238            symbols.insert(*label);
2239        }
2240        Transition::Range { start, stop, .. } => {
2241            symbols.extend_range(*start, *stop);
2242        }
2243        Transition::Set { set, .. } => {
2244            for (start, stop) in set.ranges() {
2245                symbols.extend_range(start, stop);
2246            }
2247        }
2248        Transition::NotSet { set, .. } => {
2249            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2250        }
2251        Transition::Wildcard { .. } => {
2252            symbols.extend_range(1, max_token_type);
2253        }
2254        Transition::Epsilon { .. }
2255        | Transition::Rule { .. }
2256        | Transition::Predicate { .. }
2257        | Transition::Action { .. }
2258        | Transition::Precedence { .. } => {}
2259    }
2260    symbols
2261}
2262
2263/// Returns the consuming-token expectations reachable from an ATN state through
2264/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2265/// and loop exits report the same expectation set ANTLR users see.
2266fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2267    let mut symbols = BTreeSet::new();
2268    let mut stack = vec![state_number];
2269    let mut visited = BTreeSet::new();
2270    while let Some(current) = stack.pop() {
2271        if !visited.insert(current) {
2272            continue;
2273        }
2274        let Some(state) = atn.state(current) else {
2275            continue;
2276        };
2277        for transition in &state.transitions() {
2278            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2279            if transition_symbols.is_empty() {
2280                if transition.is_epsilon() {
2281                    stack.push(transition.target());
2282                }
2283            } else {
2284                symbols.extend(transition_symbols);
2285            }
2286        }
2287    }
2288    symbols
2289}
2290
2291fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2292    let mut symbols = TokenBitSet::default();
2293    let mut stack = vec![state_number];
2294    let mut visited = BTreeSet::new();
2295    while let Some(current) = stack.pop() {
2296        if !visited.insert(current) {
2297            continue;
2298        }
2299        let Some(state) = atn.state(current) else {
2300            continue;
2301        };
2302        for transition in &state.transitions() {
2303            let transition_symbols =
2304                transition_expected_token_set(transition, atn.max_token_type());
2305            if transition_symbols.is_empty() {
2306                if transition.is_epsilon() {
2307                    stack.push(transition.target());
2308                }
2309            } else {
2310                symbols.extend_from(&transition_symbols);
2311            }
2312        }
2313    }
2314    symbols
2315}
2316
2317fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2318    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2319        return false;
2320    };
2321    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2322        return false;
2323    };
2324    epsilon_reaches_state(atn, state_number, stop_state)
2325}
2326
2327fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2328    let mut stack = vec![start];
2329    let mut visited = BTreeSet::new();
2330    while let Some(current) = stack.pop() {
2331        if current == target {
2332            return true;
2333        }
2334        if !visited.insert(current) {
2335            continue;
2336        }
2337        let Some(state) = atn.state(current) else {
2338            continue;
2339        };
2340        stack.extend(
2341            state
2342                .transitions()
2343                .iter()
2344                .filter(|transition| transition.is_epsilon())
2345                .map(ParserTransition::target),
2346        );
2347    }
2348    false
2349}
2350
2351/// FIRST set for a rule entry plus whether the rule is nullable.
2352///
2353/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2354/// a consuming transition or reaches the rule's stop state. Used by the fast
2355/// recognizer to skip rule alternatives whose first-consumed token cannot
2356/// possibly match the current lookahead.
2357#[derive(Clone, Debug, Default, Eq, PartialEq)]
2358struct FirstSet {
2359    symbols: TokenBitSet,
2360    nullable: bool,
2361}
2362
2363/// Per-parser cache of FIRST sets computed during recognition. The fast path
2364/// consults this on every speculative `Transition::Rule` encounter, so the
2365/// computation must amortize across all of those calls — the FIRST set is a
2366/// pure function of the ATN, not of the input position. Cached entries are
2367/// shared via `Rc` so the recognizer never deep-copies the underlying
2368/// `BTreeSet<i32>`.
2369type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2370
2371// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2372// and decision-lookahead entries are purely functions of the grammar's
2373// ATN, so caching across parses lets repeated parsing of the same grammar
2374// (the common case for a CLI tool or language server) avoid redoing the
2375// closure work. Generated parsers hand us a `&'static Atn` whose address
2376// is stable, which is what we hash on.
2377type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2378
2379#[derive(Debug, Default)]
2380struct LeftRecursiveOperatorLookahead {
2381    /// Operator alts whose token-prefix is fully matched by this one symbol
2382    /// (then only epsilons/actions remain before the recursive RHS call).
2383    /// Safe for one-token loop-enter fast path.
2384    single_token: TokenBitSet,
2385    /// Operator alts that start with this symbol but still require more tokens
2386    /// before the operand. Must not force enter from one-token lookahead when a
2387    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2388    /// has to weigh the exit alt as well.
2389    multi_token_prefix: TokenBitSet,
2390    predicate_dependent: TokenBitSet,
2391}
2392
2393#[derive(Default)]
2394struct SharedAtnCache {
2395    first_set: FirstSetCache,
2396    decision_lookahead: DecisionLookaheadCache,
2397    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2398    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2399    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2400    rule_stop_reach: FxHashMap<usize, bool>,
2401    observable_action_transitions: Option<bool>,
2402    predicate_transitions: Option<bool>,
2403}
2404
2405thread_local! {
2406    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2407        RefCell::new(FxHashMap::default());
2408}
2409
2410/// Compound key for `SHARED_ATN_CACHES`.
2411///
2412/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2413/// pointer identifies one grammar for the program's lifetime. For the
2414/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2415/// the secondary fields below catch the pointer collision: a new grammar
2416/// would need to match all of `(states ptr, states len, max_token_type)` to
2417/// be mistaken for the dropped one. That combination changing under us
2418/// without a rebuild is implausible enough to treat as a bug; bundling them
2419/// into the key is otherwise a few extra bytes per lookup.
2420#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2421struct SharedAtnCacheKey {
2422    atn: usize,
2423    states: usize,
2424    state_count: usize,
2425    max_token_type: i32,
2426}
2427
2428impl SharedAtnCacheKey {
2429    fn for_atn(atn: &Atn) -> Self {
2430        let (states, state_count) = atn.storage_identity();
2431        Self {
2432            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2433            states,
2434            state_count,
2435            max_token_type: atn.max_token_type(),
2436        }
2437    }
2438}
2439
2440fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2441    SHARED_ATN_CACHES.with(|cell| {
2442        let key = SharedAtnCacheKey::for_atn(atn);
2443        let mut map = cell.borrow_mut();
2444        let cache = map.entry(key).or_default();
2445        f(&mut cache.first_set)
2446    })
2447}
2448
2449fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2450    SHARED_ATN_CACHES.with(|cell| {
2451        let key = SharedAtnCacheKey::for_atn(atn);
2452        let mut map = cell.borrow_mut();
2453        let cache = map.entry(key).or_default();
2454        f(cache)
2455    })
2456}
2457
2458/// Per-decision-state cached look-1 sets for each outgoing transition.
2459///
2460/// At a multi-alternative state, the recognizer would otherwise speculatively
2461/// walk every alternative even when only one can possibly accept the current
2462/// lookahead. Caching the look-1 set per transition lets us prune the
2463/// non-viable transitions before recursing — the same SLL prediction trick
2464/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2465/// filter rather than a full DFA.
2466#[derive(Debug, Default)]
2467struct DecisionLookahead {
2468    transitions: Vec<TransitionLookSet>,
2469}
2470
2471/// Look-1 information for one outgoing transition.
2472///
2473/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2474/// can reach the rule stop without consuming a token (e.g. an empty alt).
2475/// Nullable transitions cannot be pruned: they may still be the right path
2476/// when the lookahead consumes nothing further inside the current rule.
2477#[derive(Clone, Debug, Default)]
2478struct TransitionLookSet {
2479    symbols: TokenBitSet,
2480    nullable: bool,
2481}
2482
2483/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2484/// rarely-touched fields keeps the recursive helpers below the function-arity
2485/// lint and lets every nested call thread the same cache and cycle guards.
2486struct FirstSetCtx<'a> {
2487    cache: &'a mut FirstSetCache,
2488    in_progress: BTreeSet<(usize, usize)>,
2489    hit_cycle: bool,
2490}
2491
2492/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2493/// shared cache and tolerating recursive nullable rule chains. Mutually
2494/// recursive rules cannot stack-overflow because callers in flight are tracked
2495/// in `ctx.in_progress`; revisits return without recursing, and the partial
2496/// result is cached only when no cycle was detected during its computation.
2497///
2498/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2499/// rule-call probes only pay a reference bump.
2500fn rule_first_set(
2501    atn: &Atn,
2502    target: usize,
2503    rule_stop_state: usize,
2504    cache: &mut FirstSetCache,
2505) -> Rc<FirstSet> {
2506    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2507        return Rc::clone(cached);
2508    }
2509    let mut ctx = FirstSetCtx {
2510        cache,
2511        in_progress: BTreeSet::new(),
2512        hit_cycle: false,
2513    };
2514    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2515}
2516
2517fn rule_first_set_cached(
2518    atn: &Atn,
2519    target: usize,
2520    rule_stop_state: usize,
2521    ctx: &mut FirstSetCtx<'_>,
2522) -> Rc<FirstSet> {
2523    let key = (target, rule_stop_state);
2524    if let Some(cached) = ctx.cache.get(&key) {
2525        return Rc::clone(cached);
2526    }
2527    if !ctx.in_progress.insert(key) {
2528        // Cycle: a caller above is already computing this entry. Return an
2529        // empty FIRST set; that caller's traversal supplies the contributions
2530        // from the rule's other alternatives.
2531        return Rc::new(FirstSet::default());
2532    }
2533    let saved_hit_cycle = ctx.hit_cycle;
2534    ctx.hit_cycle = false;
2535    let mut first = FirstSet::default();
2536    let mut visited = BTreeSet::new();
2537    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2538    ctx.in_progress.remove(&key);
2539    let entry = Rc::new(first);
2540    if !ctx.hit_cycle {
2541        ctx.cache.insert(key, Rc::clone(&entry));
2542    }
2543    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2544    entry
2545}
2546
2547/// Returns the look-1 set for traversing `transition` while still inside the
2548/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2549/// prunes transitions whose look-1 cannot accept the current lookahead.
2550fn transition_first_set(
2551    atn: &Atn,
2552    transition: ParserTransition<'_>,
2553    rule_stop_state: usize,
2554    cache: &mut FirstSetCache,
2555) -> TransitionLookSet {
2556    match &transition.data() {
2557        Transition::Atom { label, .. } => {
2558            let mut symbols = TokenBitSet::default();
2559            symbols.insert(*label);
2560            TransitionLookSet {
2561                symbols,
2562                nullable: false,
2563            }
2564        }
2565        Transition::Range { start, stop, .. } => {
2566            let mut symbols = TokenBitSet::default();
2567            symbols.extend_range(*start, *stop);
2568            TransitionLookSet {
2569                symbols,
2570                nullable: false,
2571            }
2572        }
2573        Transition::Set { set, .. } => {
2574            let mut symbols = TokenBitSet::default();
2575            for (start, stop) in set.ranges() {
2576                symbols.extend_range(start, stop);
2577            }
2578            TransitionLookSet {
2579                symbols,
2580                nullable: false,
2581            }
2582        }
2583        Transition::NotSet { set, .. } => {
2584            let max = atn.max_token_type();
2585            let mut symbols = TokenBitSet::default();
2586            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2587            TransitionLookSet {
2588                symbols,
2589                nullable: false,
2590            }
2591        }
2592        Transition::Wildcard { .. } => {
2593            let mut symbols = TokenBitSet::default();
2594            symbols.extend_range(1, atn.max_token_type());
2595            TransitionLookSet {
2596                symbols,
2597                nullable: false,
2598            }
2599        }
2600        Transition::Epsilon { target }
2601        | Transition::Action { target, .. }
2602        | Transition::Predicate { target, .. }
2603        | Transition::Precedence { target, .. } => {
2604            // Walk the closure starting at `target` until a consuming transition
2605            // is reached or the rule stop state is hit.
2606            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2607            TransitionLookSet {
2608                symbols: first.symbols.clone(),
2609                nullable: first.nullable,
2610            }
2611        }
2612        Transition::Rule {
2613            target,
2614            rule_index,
2615            follow_state,
2616            ..
2617        } => {
2618            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2619                return TransitionLookSet::default();
2620            };
2621            let child = rule_first_set(atn, *target, child_stop, cache);
2622            let mut symbols = child.symbols.clone();
2623            let nullable = if child.nullable {
2624                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2625                symbols.extend_from(&follow.symbols);
2626                follow.nullable
2627            } else {
2628                false
2629            };
2630            TransitionLookSet { symbols, nullable }
2631        }
2632    }
2633}
2634
2635/// Reports whether `transition` can be pruned at a multi-alt state because
2636/// its cached look-1 cannot accept the current lookahead.
2637///
2638/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2639/// Rule/Precedence) so consuming transitions still reach the
2640/// `matches`+recovery path that surfaces single-token deletion / insertion
2641/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2642/// transition is pruned, its FIRST set is folded into `expected` so failed
2643/// parses produce the same `mismatched input ... expecting ...` diagnostic
2644/// the no-prefilter baseline would emit.
2645/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2646/// one transition's FIRST set and no transition is nullable. Used as an
2647/// LL(1) commit point: when prediction is unambiguous from the lookahead
2648/// alone, the recursive recognizer can skip every other alt without paying
2649/// for the per-transition filter probe.
2650///
2651/// `None` signals the caller to fall back to per-transition lookahead
2652/// filtering. Returning `Some` for an alt whose transition cannot actually
2653/// match would prune the only viable parse path; this is why we require
2654/// strict disjointness *and* no nullable transitions in the decision.
2655fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2656    let mut chosen: Option<usize> = None;
2657    for (index, transition) in entry.transitions.iter().enumerate() {
2658        if transition.nullable {
2659            return None;
2660        }
2661        if transition.symbols.contains(symbol) {
2662            if chosen.is_some() {
2663                return None;
2664            }
2665            chosen = Some(index);
2666        }
2667    }
2668    chosen
2669}
2670
2671/// Returns the unique greedy alt index (0-based) selected by the current
2672/// lookahead.
2673///
2674/// The shortcut is intentionally conservative around nullable exits. If the
2675/// current symbol can start a consuming alternative and an empty alternative is
2676/// also present, one-token lookahead is not enough to know whether the symbol
2677/// belongs to the current construct or to its caller's follow set. `None`
2678/// signals the caller to fall back to adaptive prediction.
2679fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2680    let mut matching_non_nullable_alt = None;
2681    let mut nullable_alt = None;
2682    for (index, transition) in entry.transitions.iter().enumerate() {
2683        if transition.nullable {
2684            if nullable_alt.is_some() {
2685                return None;
2686            }
2687            nullable_alt = Some(index);
2688        }
2689        if transition.symbols.contains(symbol) {
2690            if transition.nullable {
2691                continue;
2692            }
2693            if matching_non_nullable_alt.is_some() {
2694                return None;
2695            }
2696            matching_non_nullable_alt = Some(index);
2697        }
2698    }
2699    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
2700        return None;
2701    }
2702    if non_greedy {
2703        nullable_alt.or(matching_non_nullable_alt)
2704    } else {
2705        matching_non_nullable_alt.or(nullable_alt)
2706    }
2707}
2708
2709fn should_skip_via_lookahead(
2710    transition_kind: ParserTransitionKind,
2711    transition_index: usize,
2712    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
2713    index: usize,
2714    record_expected: bool,
2715    expected: &mut ExpectedTokens,
2716) -> bool {
2717    let prune_non_consuming = matches!(
2718        transition_kind,
2719        ParserTransitionKind::Epsilon
2720            | ParserTransitionKind::Action
2721            | ParserTransitionKind::Predicate
2722            | ParserTransitionKind::Rule
2723            | ParserTransitionKind::Precedence
2724    );
2725    if !prune_non_consuming {
2726        return false;
2727    }
2728    let Some((symbol, entry)) = lookahead_filter else {
2729        return false;
2730    };
2731    let Some(set) = entry.transitions.get(transition_index) else {
2732        return false;
2733    };
2734    if set.symbols.contains(*symbol) || set.nullable {
2735        return false;
2736    }
2737    if record_expected && !set.symbols.is_empty() {
2738        record_pruned_transition_expected(set, index, expected);
2739    }
2740    true
2741}
2742
2743fn should_skip_rule_via_first_set(
2744    first: &FirstSet,
2745    symbol: i32,
2746    record_expected: bool,
2747    index: usize,
2748    expected: &mut ExpectedTokens,
2749) -> bool {
2750    if first.nullable || first.symbols.contains(symbol) {
2751        return false;
2752    }
2753    if record_expected && !first.symbols.is_empty() {
2754        record_token_bit_expected(&first.symbols, index, expected);
2755    }
2756    true
2757}
2758
2759fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
2760    match expected.index {
2761        Some(current) if index < current => {}
2762        Some(current) if index == current => {
2763            symbols.extend_btree_set(&mut expected.symbols);
2764        }
2765        _ => {
2766            expected.index = Some(index);
2767            expected.symbols = symbols.to_btree_set();
2768        }
2769    }
2770}
2771
2772/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
2773fn record_pruned_transition_expected(
2774    set: &TransitionLookSet,
2775    index: usize,
2776    expected: &mut ExpectedTokens,
2777) {
2778    match expected.index {
2779        Some(current) if index < current => {}
2780        Some(current) if index == current => {
2781            set.symbols.extend_btree_set(&mut expected.symbols);
2782        }
2783        _ => {
2784            expected.index = Some(index);
2785            expected.symbols = set.symbols.to_btree_set();
2786        }
2787    }
2788}
2789
2790fn rule_first_set_inner(
2791    atn: &Atn,
2792    state_number: usize,
2793    rule_stop_state: usize,
2794    ctx: &mut FirstSetCtx<'_>,
2795    visited: &mut BTreeSet<usize>,
2796    first: &mut FirstSet,
2797) {
2798    if !visited.insert(state_number) {
2799        return;
2800    }
2801    if state_number == rule_stop_state {
2802        first.nullable = true;
2803        return;
2804    }
2805    let Some(state) = atn.state(state_number) else {
2806        return;
2807    };
2808    for transition in &state.transitions() {
2809        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2810        if !transition_symbols.is_empty() {
2811            first.symbols.extend_iter(transition_symbols);
2812            continue;
2813        }
2814        match &transition.data() {
2815            Transition::Epsilon { target }
2816            | Transition::Action { target, .. }
2817            | Transition::Predicate { target, .. }
2818            | Transition::Precedence { target, .. } => {
2819                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
2820            }
2821            Transition::Rule {
2822                target,
2823                rule_index,
2824                follow_state,
2825                ..
2826            } => {
2827                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2828                    continue;
2829                };
2830                let child_key = (*target, child_stop);
2831                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
2832                    ctx.hit_cycle = true;
2833                }
2834                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
2835                first.symbols.extend_from(&child.symbols);
2836                if child.nullable {
2837                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
2838                }
2839            }
2840            Transition::Atom { .. }
2841            | Transition::Range { .. }
2842            | Transition::Set { .. }
2843            | Transition::NotSet { .. }
2844            | Transition::Wildcard { .. } => {}
2845        }
2846    }
2847}
2848
2849/// Returns token types that can resume parsing from `state_number` after a
2850/// failed child rule, following rule calls as well as epsilon transitions.
2851fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
2852    let mut symbols = BTreeSet::new();
2853    state_sync_symbols_inner(
2854        atn,
2855        state_number,
2856        stop_state,
2857        &mut BTreeSet::new(),
2858        &mut symbols,
2859    );
2860    symbols
2861}
2862
2863/// Walks epsilon-like continuations from a parent follow state until it finds
2864/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
2865fn state_sync_symbols_inner(
2866    atn: &Atn,
2867    state_number: usize,
2868    stop_state: usize,
2869    visited: &mut BTreeSet<usize>,
2870    symbols: &mut BTreeSet<i32>,
2871) {
2872    if !visited.insert(state_number) {
2873        return;
2874    }
2875    if state_number == stop_state {
2876        symbols.insert(TOKEN_EOF);
2877        return;
2878    }
2879    let Some(state) = atn.state(state_number) else {
2880        return;
2881    };
2882    for transition in &state.transitions() {
2883        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2884        if transition_symbols.is_empty() {
2885            match &transition.data() {
2886                Transition::Rule { target, .. }
2887                | Transition::Epsilon { target }
2888                | Transition::Action { target, .. }
2889                | Transition::Predicate { target, .. }
2890                | Transition::Precedence { target, .. } => {
2891                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
2892                }
2893                Transition::Atom { .. }
2894                | Transition::Range { .. }
2895                | Transition::Set { .. }
2896                | Transition::NotSet { .. }
2897                | Transition::Wildcard { .. } => {}
2898            }
2899        } else {
2900            symbols.extend(transition_symbols);
2901        }
2902    }
2903}
2904
2905#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2906struct OperatorSymbolReachability {
2907    /// One token completes an unconditional operator token-prefix.
2908    single_token: bool,
2909    /// An unconditional operator path requires more tokens before its operand.
2910    multi_token: bool,
2911    /// At least one matching operator path depends on a semantic predicate.
2912    predicate_dependent: bool,
2913}
2914
2915impl OperatorSymbolReachability {
2916    const ADAPTIVE_FALLBACK: Self = Self {
2917        single_token: false,
2918        multi_token: false,
2919        predicate_dependent: true,
2920    };
2921
2922    const fn single_token(predicate_dependent: bool) -> Self {
2923        if predicate_dependent {
2924            Self {
2925                single_token: false,
2926                multi_token: false,
2927                predicate_dependent: true,
2928            }
2929        } else {
2930            Self {
2931                single_token: true,
2932                multi_token: false,
2933                predicate_dependent: false,
2934            }
2935        }
2936    }
2937
2938    const fn multi_token(predicate_dependent: bool) -> Self {
2939        if predicate_dependent {
2940            Self {
2941                single_token: false,
2942                multi_token: false,
2943                predicate_dependent: true,
2944            }
2945        } else {
2946            Self {
2947                single_token: false,
2948                multi_token: true,
2949                predicate_dependent: false,
2950            }
2951        }
2952    }
2953
2954    const fn union(self, other: Self) -> Self {
2955        Self {
2956            single_token: self.single_token || other.single_token,
2957            multi_token: self.multi_token || other.multi_token,
2958            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
2959        }
2960    }
2961}
2962
2963#[derive(Clone, Copy)]
2964struct OperatorReachabilityRequest {
2965    symbol: i32,
2966    precedence: i32,
2967    predicate_dependent: bool,
2968    operator_rule_index: usize,
2969}
2970
2971#[derive(Clone, Copy, Debug)]
2972struct OperatorRuleContinuation {
2973    stop_state: usize,
2974    follow_state: usize,
2975    return_precedence: i32,
2976}
2977
2978struct NullablePrecedenceCtx {
2979    cache: FxHashMap<(usize, usize, i32, bool), bool>,
2980    in_progress: BTreeSet<(usize, usize, i32, bool)>,
2981    hit_cycle: bool,
2982}
2983
2984fn state_is_nullable_with_precedence(
2985    atn: &Atn,
2986    state_number: usize,
2987    stop_state_number: usize,
2988    precedence: i32,
2989    allow_predicates: bool,
2990    ctx: &mut NullablePrecedenceCtx,
2991) -> bool {
2992    let saved_hit_cycle = ctx.hit_cycle;
2993    ctx.hit_cycle = false;
2994    let nullable = state_is_nullable_with_precedence_cached(
2995        atn,
2996        state_number,
2997        stop_state_number,
2998        precedence,
2999        allow_predicates,
3000        ctx,
3001    );
3002    ctx.hit_cycle = saved_hit_cycle;
3003    nullable
3004}
3005
3006fn state_is_nullable_with_precedence_cached(
3007    atn: &Atn,
3008    state_number: usize,
3009    stop_state_number: usize,
3010    precedence: i32,
3011    allow_predicates: bool,
3012    ctx: &mut NullablePrecedenceCtx,
3013) -> bool {
3014    if state_number == stop_state_number {
3015        return true;
3016    }
3017    let key = (
3018        state_number,
3019        stop_state_number,
3020        precedence,
3021        allow_predicates,
3022    );
3023    if let Some(cached) = ctx.cache.get(&key) {
3024        return *cached;
3025    }
3026    if !ctx.in_progress.insert(key) {
3027        ctx.hit_cycle = true;
3028        return false;
3029    }
3030    let saved_hit_cycle = ctx.hit_cycle;
3031    ctx.hit_cycle = false;
3032    let nullable = atn.state(state_number).is_some_and(|state| {
3033        state
3034            .transitions()
3035            .iter()
3036            .any(|transition| match &transition.data() {
3037                Transition::Rule {
3038                    target,
3039                    rule_index,
3040                    follow_state,
3041                    precedence: rule_precedence,
3042                } => {
3043                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3044                        return false;
3045                    };
3046                    state_is_nullable_with_precedence_cached(
3047                        atn,
3048                        *target,
3049                        child_stop,
3050                        *rule_precedence,
3051                        allow_predicates,
3052                        ctx,
3053                    ) && state_is_nullable_with_precedence_cached(
3054                        atn,
3055                        *follow_state,
3056                        stop_state_number,
3057                        precedence,
3058                        allow_predicates,
3059                        ctx,
3060                    )
3061                }
3062                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3063                    state_is_nullable_with_precedence_cached(
3064                        atn,
3065                        *target,
3066                        stop_state_number,
3067                        precedence,
3068                        allow_predicates,
3069                        ctx,
3070                    )
3071                }
3072                Transition::Predicate { target, .. } if allow_predicates => {
3073                    state_is_nullable_with_precedence_cached(
3074                        atn,
3075                        *target,
3076                        stop_state_number,
3077                        precedence,
3078                        allow_predicates,
3079                        ctx,
3080                    )
3081                }
3082                Transition::Precedence {
3083                    target,
3084                    precedence: transition_precedence,
3085                } if *transition_precedence >= precedence => {
3086                    state_is_nullable_with_precedence_cached(
3087                        atn,
3088                        *target,
3089                        stop_state_number,
3090                        precedence,
3091                        allow_predicates,
3092                        ctx,
3093                    )
3094                }
3095                Transition::Atom { .. }
3096                | Transition::Range { .. }
3097                | Transition::Set { .. }
3098                | Transition::NotSet { .. }
3099                | Transition::Wildcard { .. }
3100                | Transition::Predicate { .. }
3101                | Transition::Precedence { .. } => false,
3102            })
3103    });
3104    ctx.in_progress.remove(&key);
3105    if !ctx.hit_cycle {
3106        ctx.cache.insert(key, nullable);
3107    }
3108    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3109    nullable
3110}
3111
3112/// Classifies what remains after the operator's first token is matched.
3113fn state_operator_token_prefix_reachability(
3114    atn: &Atn,
3115    state_number: usize,
3116    request: OperatorReachabilityRequest,
3117    continuations: &[OperatorRuleContinuation],
3118    visited: &mut BTreeSet<(usize, i32, bool)>,
3119) -> OperatorSymbolReachability {
3120    let key = (
3121        state_number,
3122        request.precedence,
3123        request.predicate_dependent,
3124    );
3125    if !visited.insert(key) {
3126        // Recursive helper rules can grow the return stack without consuming
3127        // input. Delegate cycles to adaptive prediction instead of forcing a
3128        // potentially incomplete one-token answer.
3129        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3130    }
3131    if let Some((continuation, remaining)) = continuations.split_last()
3132        && state_number == continuation.stop_state
3133    {
3134        let result = state_operator_token_prefix_reachability(
3135            atn,
3136            continuation.follow_state,
3137            OperatorReachabilityRequest {
3138                precedence: continuation.return_precedence,
3139                ..request
3140            },
3141            remaining,
3142            visited,
3143        );
3144        visited.remove(&key);
3145        return result;
3146    }
3147    let Some(state) = atn.state(state_number) else {
3148        visited.remove(&key);
3149        return OperatorSymbolReachability::default();
3150    };
3151    let completes_operator = match state.kind() {
3152        AtnStateKind::RuleStop => continuations.is_empty(),
3153        AtnStateKind::StarLoopBack
3154        | AtnStateKind::StarLoopEntry
3155        | AtnStateKind::PlusLoopBack
3156        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3157        _ => false,
3158    };
3159    if completes_operator {
3160        visited.remove(&key);
3161        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3162    }
3163    let mut reachability = OperatorSymbolReachability::default();
3164    for transition in &state.transitions() {
3165        let transition_reachability = match &transition.data() {
3166            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3167                OperatorSymbolReachability::single_token(request.predicate_dependent)
3168            }
3169            Transition::Rule {
3170                target,
3171                rule_index,
3172                follow_state,
3173                precedence: rule_precedence,
3174            } => {
3175                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3176                    continue;
3177                };
3178                let mut nested = continuations.to_vec();
3179                nested.push(OperatorRuleContinuation {
3180                    stop_state: child_stop,
3181                    follow_state: *follow_state,
3182                    return_precedence: request.precedence,
3183                });
3184                state_operator_token_prefix_reachability(
3185                    atn,
3186                    *target,
3187                    OperatorReachabilityRequest {
3188                        precedence: *rule_precedence,
3189                        ..request
3190                    },
3191                    &nested,
3192                    visited,
3193                )
3194            }
3195            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3196                state_operator_token_prefix_reachability(
3197                    atn,
3198                    *target,
3199                    request,
3200                    continuations,
3201                    visited,
3202                )
3203            }
3204            Transition::Precedence {
3205                target,
3206                precedence: transition_precedence,
3207            } => {
3208                if *transition_precedence < request.precedence {
3209                    OperatorSymbolReachability::default()
3210                } else {
3211                    state_operator_token_prefix_reachability(
3212                        atn,
3213                        *target,
3214                        request,
3215                        continuations,
3216                        visited,
3217                    )
3218                }
3219            }
3220            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3221                atn,
3222                *target,
3223                OperatorReachabilityRequest {
3224                    predicate_dependent: true,
3225                    ..request
3226                },
3227                continuations,
3228                visited,
3229            ),
3230            Transition::Atom { .. }
3231            | Transition::Range { .. }
3232            | Transition::Set { .. }
3233            | Transition::NotSet { .. }
3234            | Transition::Wildcard { .. } => {
3235                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3236            }
3237        };
3238        reachability = reachability.union(transition_reachability);
3239    }
3240    visited.remove(&key);
3241    reachability
3242}
3243
3244fn state_can_reach_symbol_with_precedence(
3245    atn: &Atn,
3246    state_number: usize,
3247    request: OperatorReachabilityRequest,
3248    nullable_ctx: &mut NullablePrecedenceCtx,
3249    continuations: &mut Vec<OperatorRuleContinuation>,
3250    visited: &mut BTreeSet<(usize, i32, bool)>,
3251) -> OperatorSymbolReachability {
3252    let key = (
3253        state_number,
3254        request.precedence,
3255        request.predicate_dependent,
3256    );
3257    if !visited.insert(key) {
3258        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3259    }
3260    let Some(state) = atn.state(state_number) else {
3261        visited.remove(&key);
3262        return OperatorSymbolReachability::default();
3263    };
3264    let mut reachability = OperatorSymbolReachability::default();
3265    for transition in &state.transitions() {
3266        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3267            reachability = reachability.union(state_operator_token_prefix_reachability(
3268                atn,
3269                transition.target(),
3270                request,
3271                continuations,
3272                &mut BTreeSet::new(),
3273            ));
3274            continue;
3275        }
3276        let transition_reachability = match &transition.data() {
3277            Transition::Rule {
3278                target,
3279                rule_index,
3280                follow_state,
3281                precedence: rule_precedence,
3282            } => {
3283                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3284                    continue;
3285                };
3286                continuations.push(OperatorRuleContinuation {
3287                    stop_state: child_stop,
3288                    follow_state: *follow_state,
3289                    return_precedence: request.precedence,
3290                });
3291                let mut result = state_can_reach_symbol_with_precedence(
3292                    atn,
3293                    *target,
3294                    OperatorReachabilityRequest {
3295                        precedence: *rule_precedence,
3296                        ..request
3297                    },
3298                    nullable_ctx,
3299                    continuations,
3300                    visited,
3301                );
3302                continuations.pop();
3303                if state_is_nullable_with_precedence(
3304                    atn,
3305                    *target,
3306                    child_stop,
3307                    *rule_precedence,
3308                    true,
3309                    nullable_ctx,
3310                ) {
3311                    let child_predicate_dependent = request.predicate_dependent
3312                        || !state_is_nullable_with_precedence(
3313                            atn,
3314                            *target,
3315                            child_stop,
3316                            *rule_precedence,
3317                            false,
3318                            nullable_ctx,
3319                        );
3320                    result = result.union(state_can_reach_symbol_with_precedence(
3321                        atn,
3322                        *follow_state,
3323                        OperatorReachabilityRequest {
3324                            predicate_dependent: child_predicate_dependent,
3325                            ..request
3326                        },
3327                        nullable_ctx,
3328                        continuations,
3329                        visited,
3330                    ));
3331                }
3332                result
3333            }
3334            Transition::Epsilon { target }
3335            | Transition::Action { target, .. }
3336            | Transition::Precedence { target, .. } => {
3337                if matches!(
3338                    &transition.data(),
3339                    Transition::Precedence {
3340                        precedence: transition_precedence,
3341                        ..
3342                    } if *transition_precedence < request.precedence
3343                ) {
3344                    continue;
3345                }
3346                state_can_reach_symbol_with_precedence(
3347                    atn,
3348                    *target,
3349                    request,
3350                    nullable_ctx,
3351                    continuations,
3352                    visited,
3353                )
3354            }
3355            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3356                atn,
3357                *target,
3358                OperatorReachabilityRequest {
3359                    predicate_dependent: true,
3360                    ..request
3361                },
3362                nullable_ctx,
3363                continuations,
3364                visited,
3365            ),
3366            Transition::Atom { .. }
3367            | Transition::Range { .. }
3368            | Transition::Set { .. }
3369            | Transition::NotSet { .. }
3370            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3371        };
3372        reachability = reachability.union(transition_reachability);
3373    }
3374    visited.remove(&key);
3375    reachability
3376}
3377
3378fn left_recursive_operator_lookahead(
3379    atn: &Atn,
3380    state_number: usize,
3381    precedence: i32,
3382) -> LeftRecursiveOperatorLookahead {
3383    let Some(state) = atn.state(state_number) else {
3384        return LeftRecursiveOperatorLookahead::default();
3385    };
3386    let Some(operator_rule_index) = state.rule_index() else {
3387        return LeftRecursiveOperatorLookahead::default();
3388    };
3389    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3390    let mut nullable_ctx = NullablePrecedenceCtx {
3391        cache: FxHashMap::default(),
3392        in_progress: BTreeSet::new(),
3393        hit_cycle: false,
3394    };
3395    for transition in &state.transitions() {
3396        let target = transition.target();
3397        if atn
3398            .state(target)
3399            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3400        {
3401            continue;
3402        }
3403        for symbol in 1..=atn.max_token_type() {
3404            let reachability = state_can_reach_symbol_with_precedence(
3405                atn,
3406                target,
3407                OperatorReachabilityRequest {
3408                    symbol,
3409                    precedence,
3410                    predicate_dependent: false,
3411                    operator_rule_index,
3412                },
3413                &mut nullable_ctx,
3414                &mut Vec::new(),
3415                &mut BTreeSet::new(),
3416            );
3417            if reachability.single_token {
3418                lookahead.single_token.insert(symbol);
3419            }
3420            if reachability.multi_token {
3421                lookahead.multi_token_prefix.insert(symbol);
3422            }
3423            if reachability.predicate_dependent {
3424                lookahead.predicate_dependent.insert(symbol);
3425            }
3426        }
3427    }
3428    lookahead
3429}
3430
3431#[derive(Debug, Default)]
3432struct StateBeforeStopLookahead {
3433    symbols: TokenBitSet,
3434    reaches_context_boundary: bool,
3435}
3436
3437fn state_before_stop_lookahead(
3438    atn: &Atn,
3439    state_number: usize,
3440    stop_state_number: usize,
3441) -> Rc<StateBeforeStopLookahead> {
3442    with_shared_atn_caches(atn, |cache| {
3443        let key = (state_number, stop_state_number);
3444        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3445            return Rc::clone(cached);
3446        }
3447        let mut lookahead = StateBeforeStopLookahead::default();
3448        state_before_stop_lookahead_inner(
3449            atn,
3450            state_number,
3451            stop_state_number,
3452            &mut BTreeSet::new(),
3453            &mut cache.first_set,
3454            &mut lookahead,
3455        );
3456        let lookahead = Rc::new(lookahead);
3457        cache
3458            .state_before_stop_lookahead
3459            .insert(key, Rc::clone(&lookahead));
3460        lookahead
3461    })
3462}
3463
3464fn state_before_stop_lookahead_inner(
3465    atn: &Atn,
3466    state_number: usize,
3467    stop_state_number: usize,
3468    visited: &mut BTreeSet<usize>,
3469    first_set_cache: &mut FirstSetCache,
3470    lookahead: &mut StateBeforeStopLookahead,
3471) {
3472    if state_number == stop_state_number {
3473        lookahead.reaches_context_boundary = true;
3474        return;
3475    }
3476    if !visited.insert(state_number) {
3477        return;
3478    }
3479    let Some(state) = atn.state(state_number) else {
3480        return;
3481    };
3482    if state.kind() == AtnStateKind::RuleStop {
3483        lookahead.reaches_context_boundary = true;
3484        return;
3485    }
3486    for transition in &state.transitions() {
3487        match &transition.data() {
3488            Transition::Epsilon { target }
3489            | Transition::Action { target, .. }
3490            | Transition::Predicate { target, .. }
3491            | Transition::Precedence { target, .. } => {
3492                state_before_stop_lookahead_inner(
3493                    atn,
3494                    *target,
3495                    stop_state_number,
3496                    visited,
3497                    first_set_cache,
3498                    lookahead,
3499                );
3500            }
3501            Transition::Rule {
3502                target,
3503                rule_index,
3504                follow_state,
3505                ..
3506            } => {
3507                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3508                    continue;
3509                };
3510                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3511                lookahead.symbols.extend_from(&child.symbols);
3512                if child.nullable {
3513                    state_before_stop_lookahead_inner(
3514                        atn,
3515                        *follow_state,
3516                        stop_state_number,
3517                        visited,
3518                        first_set_cache,
3519                        lookahead,
3520                    );
3521                }
3522            }
3523            Transition::Atom { .. }
3524            | Transition::Range { .. }
3525            | Transition::Set { .. }
3526            | Transition::NotSet { .. }
3527            | Transition::Wildcard { .. } => {
3528                lookahead.symbols.extend_iter(transition_expected_symbols(
3529                    transition,
3530                    atn.max_token_type(),
3531                ));
3532            }
3533        }
3534    }
3535}
3536
3537fn caller_context_can_match_symbol_before_state(
3538    atn: &Atn,
3539    return_states: impl DoubleEndedIterator<Item = usize>,
3540    stop_state_number: usize,
3541    symbol: i32,
3542) -> bool {
3543    for return_state in return_states.rev() {
3544        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3545        if lookahead.symbols.contains(symbol) {
3546            return true;
3547        }
3548        if !lookahead.reaches_context_boundary {
3549            return false;
3550        }
3551    }
3552    false
3553}
3554
3555/// Carries recovery expectations and their restart state through epsilon-only
3556/// paths. ANTLR can report and repair at the decision state even when the
3557/// failed consuming transition is nested under block or loop epsilon edges.
3558fn next_recovery_context(
3559    atn: &Atn,
3560    state: AtnState<'_>,
3561    inherited: &BTreeSet<i32>,
3562    inherited_state: Option<usize>,
3563) -> (BTreeSet<i32>, Option<usize>) {
3564    let state_symbols = state_expected_symbols(atn, state.state_number());
3565    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3566        let mut symbols = state_symbols;
3567        symbols.extend(inherited.iter().copied());
3568        return (symbols, Some(state.state_number()));
3569    }
3570    (inherited.clone(), inherited_state)
3571}
3572
3573fn recovery_expected_symbols(
3574    atn: &Atn,
3575    state_number: usize,
3576    inherited: &BTreeSet<i32>,
3577) -> BTreeSet<i32> {
3578    let mut symbols = state_expected_symbols(atn, state_number);
3579    symbols.extend(inherited.iter().copied());
3580    symbols
3581}
3582
3583/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3584/// parser's cached state-expected-symbols sets and the inherited `Rc`
3585/// without copying when the state cannot widen recovery.
3586fn fast_next_recovery_context<S, H>(
3587    parser: &mut BaseParser<S, H>,
3588    atn: &Atn,
3589    state: AtnState<'_>,
3590    inherited: &Rc<BTreeSet<i32>>,
3591    inherited_state: Option<usize>,
3592) -> (Rc<BTreeSet<i32>>, Option<usize>)
3593where
3594    S: TokenSource,
3595    H: SemanticHooks,
3596{
3597    if state.transitions().len() <= 1 {
3598        return (Rc::clone(inherited), inherited_state);
3599    }
3600    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3601    if state_symbols.is_empty() {
3602        return (Rc::clone(inherited), inherited_state);
3603    }
3604    if inherited.is_empty() {
3605        return (state_symbols, Some(state.state_number()));
3606    }
3607    if Rc::ptr_eq(&state_symbols, inherited) {
3608        return (state_symbols, Some(state.state_number()));
3609    }
3610    let mut combined = (*state_symbols).clone();
3611    combined.extend(inherited.iter().copied());
3612    (
3613        parser.intern_recovery_symbols(combined),
3614        Some(state.state_number()),
3615    )
3616}
3617
3618/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3619/// cached state-expected-symbols and avoids cloning when no widening is
3620/// needed.
3621fn fast_recovery_expected_symbols<S, H>(
3622    parser: &mut BaseParser<S, H>,
3623    atn: &Atn,
3624    state_number: usize,
3625    inherited: &Rc<BTreeSet<i32>>,
3626) -> Rc<BTreeSet<i32>>
3627where
3628    S: TokenSource,
3629    H: SemanticHooks,
3630{
3631    let cached = parser.cached_state_expected_symbols(atn, state_number);
3632    if inherited.is_empty() {
3633        return cached;
3634    }
3635    if cached.is_empty() {
3636        return Rc::clone(inherited);
3637    }
3638    if Rc::ptr_eq(&cached, inherited) {
3639        return cached;
3640    }
3641    let mut combined = (*cached).clone();
3642    combined.extend(inherited.iter().copied());
3643    parser.intern_recovery_symbols(combined)
3644}
3645
3646struct ParserTableSemCtx<'a> {
3647    member_values: &'a mut BTreeMap<usize, i64>,
3648    return_values: &'a mut BTreeMap<String, i64>,
3649}
3650
3651impl semir::PredContext for ParserTableSemCtx<'_> {
3652    type TokenText<'a>
3653        = &'a str
3654    where
3655        Self: 'a;
3656
3657    fn la(&mut self, _offset: isize) -> i64 {
3658        i64::from(TOKEN_EOF)
3659    }
3660
3661    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3662        None
3663    }
3664
3665    fn token_index_adjacent(&mut self) -> bool {
3666        false
3667    }
3668
3669    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3670        None
3671    }
3672
3673    fn member(&self, member: usize) -> Option<i64> {
3674        Some(self.member_values.get(&member).copied().unwrap_or_default())
3675    }
3676
3677    fn local_arg(&self) -> Option<i64> {
3678        None
3679    }
3680
3681    fn column(&self) -> Option<i64> {
3682        None
3683    }
3684
3685    fn token_start_column(&self) -> Option<i64> {
3686        None
3687    }
3688
3689    fn token_text_so_far(&self) -> Option<String> {
3690        None
3691    }
3692
3693    fn hook(&mut self, _hook: HookId) -> bool {
3694        false
3695    }
3696}
3697
3698impl semir::ActContext for ParserTableSemCtx<'_> {
3699    fn set_member(&mut self, member: usize, value: i64) {
3700        self.member_values.insert(member, value);
3701    }
3702
3703    fn set_return(&mut self, name: &str, value: i64) {
3704        self.return_values.insert(name.to_owned(), value);
3705    }
3706
3707    fn action_hook(&mut self, _hook: HookId) {}
3708}
3709
3710/// Applies generated integer-member side effects to one speculative path.
3711fn apply_member_actions(
3712    source_state: usize,
3713    actions: &[ParserMemberAction],
3714    semantics: Option<&ParserSemantics>,
3715    values: &mut BTreeMap<usize, i64>,
3716) {
3717    for action in actions
3718        .iter()
3719        .filter(|action| action.source_state == source_state)
3720    {
3721        *values.entry(action.member).or_default() += action.delta;
3722    }
3723    let Some(semantics) = semantics else {
3724        return;
3725    };
3726    let mut return_values = BTreeMap::new();
3727    let mut ctx = ParserTableSemCtx {
3728        member_values: values,
3729        return_values: &mut return_values,
3730    };
3731    for action in semantics
3732        .actions
3733        .iter()
3734        .filter(|action| action.source_state == source_state && action.speculative)
3735    {
3736        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3737    }
3738}
3739
3740/// Returns the speculative member state after replaying one ATN action state.
3741fn member_values_after_action(
3742    source_state: usize,
3743    actions: &[ParserMemberAction],
3744    semantics: Option<&ParserSemantics>,
3745    values: &BTreeMap<usize, i64>,
3746) -> BTreeMap<usize, i64> {
3747    let mut values = values.clone();
3748    apply_member_actions(source_state, actions, semantics, &mut values);
3749    values
3750}
3751
3752/// Returns the speculative rule-return state after replaying one ATN action.
3753fn return_values_after_action(
3754    source_state: usize,
3755    rule_index: usize,
3756    actions: &[ParserReturnAction],
3757    semantics: Option<&ParserSemantics>,
3758    values: &BTreeMap<String, i64>,
3759) -> BTreeMap<String, i64> {
3760    let mut values = values.clone();
3761    for action in actions
3762        .iter()
3763        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
3764    {
3765        values.insert(action.name.to_owned(), action.value);
3766    }
3767    if let Some(semantics) = semantics {
3768        let mut member_values = BTreeMap::new();
3769        let mut ctx = ParserTableSemCtx {
3770            member_values: &mut member_values,
3771            return_values: &mut values,
3772        };
3773        for action in semantics.actions.iter().filter(|action| {
3774            action.source_state == source_state
3775                && action.rule_index == rule_index
3776                && !action.speculative
3777        }) {
3778            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3779        }
3780    }
3781    values
3782}
3783
3784/// Resolves the integer argument visible to a child rule invocation.
3785fn rule_local_int_arg(
3786    rule_args: &[ParserRuleArg],
3787    source_state: usize,
3788    rule_index: usize,
3789    local_int_arg: Option<(usize, i64)>,
3790) -> Option<(usize, i64)> {
3791    rule_args
3792        .iter()
3793        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
3794        .map(|arg| {
3795            let value = if arg.inherit_local {
3796                local_int_arg.map_or(arg.value, |(_, value)| value)
3797            } else {
3798                arg.value
3799            };
3800            (rule_index, value)
3801        })
3802}
3803
3804/// Builds the terminal recognition outcome for a path that reached its stop
3805/// state.
3806fn stop_outcome(
3807    index: usize,
3808    consumed_eof: bool,
3809    rule_alt_number: usize,
3810    member_values: BTreeMap<usize, i64>,
3811    return_values: BTreeMap<String, i64>,
3812) -> Vec<RecognizeOutcome> {
3813    vec![RecognizeOutcome {
3814        index,
3815        consumed_eof,
3816        alt_number: rule_alt_number,
3817        member_values,
3818        return_values,
3819        diagnostics: DiagnosticSeqId::EMPTY,
3820        decisions: Vec::new(),
3821        actions: Vec::new(),
3822        nodes: NodeSeqId::EMPTY,
3823    }]
3824}
3825
3826fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
3827    with_shared_atn_caches(atn, |cache| {
3828        *cache.observable_action_transitions.get_or_insert_with(|| {
3829            atn.states().any(|state| {
3830                state.transitions().iter().any(|transition| {
3831                    matches!(
3832                        &transition.data(),
3833                        Transition::Action {
3834                            action_index: Some(_),
3835                            ..
3836                        }
3837                    )
3838                })
3839            })
3840        })
3841    })
3842}
3843
3844fn atn_has_predicate_transitions(atn: &Atn) -> bool {
3845    with_shared_atn_caches(atn, |cache| {
3846        *cache.predicate_transitions.get_or_insert_with(|| {
3847            atn.states().any(|state| {
3848                state
3849                    .transitions()
3850                    .iter()
3851                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
3852            })
3853        })
3854    })
3855}
3856
3857/// Reports whether predicates are the only observable semantics the fast
3858/// recognizer must preserve. Without path-local actions, arguments, or return
3859/// state, repeated evaluation at one coordinate and input index receives the
3860/// same runtime context.
3861fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
3862    options.init_action_rules.is_empty()
3863        && !options.track_alt_numbers
3864        && options
3865            .predicates
3866            .iter()
3867            .all(|(_, _, predicate)| predicate.failure_message().is_none())
3868        && options.semantics.is_none_or(|semantics| {
3869            semantics.actions.is_empty()
3870                && semantics
3871                    .predicates
3872                    .iter()
3873                    .all(|predicate| predicate.failure_message.is_none())
3874        })
3875        && options.rule_args.is_empty()
3876        && options.member_actions.is_empty()
3877        && options.return_actions.is_empty()
3878        && !atn_has_observable_action_transitions(atn)
3879}
3880
3881#[derive(Clone, Debug, Eq, PartialEq)]
3882struct RecognizeRequest<'a> {
3883    state_number: usize,
3884    stop_state: usize,
3885    index: usize,
3886    rule_start_index: usize,
3887    decision_start_index: Option<usize>,
3888    init_action_rules: &'a BTreeSet<usize>,
3889    predicates: &'a [(usize, usize, ParserPredicate)],
3890    semantics: Option<&'a ParserSemantics>,
3891    rule_args: &'a [ParserRuleArg],
3892    member_actions: &'a [ParserMemberAction],
3893    return_actions: &'a [ParserReturnAction],
3894    local_int_arg: Option<(usize, i64)>,
3895    member_values: BTreeMap<usize, i64>,
3896    return_values: BTreeMap<String, i64>,
3897    rule_alt_number: usize,
3898    track_alt_numbers: bool,
3899    consumed_eof: bool,
3900    /// Current left-recursive precedence threshold, matching ANTLR's
3901    /// `precpred(_ctx, k)` check for generated precedence rules.
3902    precedence: i32,
3903    depth: usize,
3904    recovery_symbols: BTreeSet<i32>,
3905    recovery_state: Option<usize>,
3906}
3907
3908#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
3909struct RecognizeKey {
3910    state_number: usize,
3911    stop_state: usize,
3912    index: usize,
3913    rule_start_index: usize,
3914    decision_start_index: Option<usize>,
3915    local_int_arg: Option<(usize, i64)>,
3916    member_values: BTreeMap<usize, i64>,
3917    return_values: BTreeMap<String, i64>,
3918    rule_alt_number: usize,
3919    track_alt_numbers: bool,
3920    consumed_eof: bool,
3921    precedence: i32,
3922    recovery_symbols: BTreeSet<i32>,
3923    recovery_state: Option<usize>,
3924}
3925
3926#[derive(Clone, Debug, Eq, PartialEq)]
3927struct EpsilonActionStep {
3928    source_state: usize,
3929    target: usize,
3930    action_rule_index: Option<usize>,
3931    left_recursive_boundary: Option<usize>,
3932    decision: Option<usize>,
3933    decision_start_index: Option<usize>,
3934    alt_number: usize,
3935    recovery_symbols: BTreeSet<i32>,
3936    recovery_state: Option<usize>,
3937}
3938
3939struct RecognizeScratch<'a> {
3940    visiting: &'a mut BTreeSet<RecognizeKey>,
3941    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
3942    expected: &'a mut ExpectedTokens,
3943}
3944
3945#[derive(Clone, Debug, Eq, PartialEq)]
3946struct FastRecognizeRequest {
3947    state_number: usize,
3948    stop_state: usize,
3949    index: usize,
3950    rule_start_index: usize,
3951    decision_start_index: Option<usize>,
3952    precedence: i32,
3953    depth: usize,
3954    recovery_symbols: Rc<BTreeSet<i32>>,
3955    recovery_state: Option<usize>,
3956}
3957
3958#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3959struct FastRecognizeTopRequest {
3960    start_state: usize,
3961    stop_state: usize,
3962    start_index: usize,
3963    precedence: i32,
3964    caller_follow_state: Option<usize>,
3965}
3966
3967#[derive(Clone, Copy, Debug)]
3968struct FastPredicateContext<'a> {
3969    predicates: &'a [(usize, usize, ParserPredicate)],
3970    semantics: Option<&'a ParserSemantics>,
3971    member_values: &'a BTreeMap<usize, i64>,
3972}
3973
3974struct FastRecognizeScratch<'a, 'b> {
3975    predicate_context: Option<FastPredicateContext<'a>>,
3976    visiting: &'b mut FxHashSet<FastRecognizeKey>,
3977    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
3978    expected: &'b mut ExpectedTokens,
3979}
3980
3981#[derive(Clone, Copy, Debug)]
3982struct FastRepetitionShape {
3983    enter_target: usize,
3984    exit_target: usize,
3985    body_stop_state: usize,
3986    enter_transition_index: usize,
3987    exit_transition_index: usize,
3988}
3989
3990#[derive(Clone, Copy, Debug)]
3991struct FastRepetitionPath {
3992    index: usize,
3993    deferred_nodes: FastDeferredNodeId,
3994    diagnostics: DiagnosticSeqId,
3995    consumed_eof: bool,
3996}
3997
3998enum FastRepetitionWork {
3999    Enter(FastRepetitionPath),
4000    Exit(FastRepetitionPath),
4001}
4002
4003/// Dense entered/exited coordinate sets for one repetition walk.
4004///
4005/// The start coordinate stays inline so short loops avoid a heap allocation;
4006/// later token indexes use one byte each instead of two hash-table entries.
4007struct FastRepetitionCoordinates {
4008    base_index: usize,
4009    base_state: u8,
4010    later_states: Vec<u8>,
4011}
4012
4013impl FastRepetitionCoordinates {
4014    const ENTERED: u8 = 0;
4015    const EXITED: u8 = 2;
4016
4017    const fn new(base_index: usize) -> Self {
4018        Self {
4019            base_index,
4020            base_state: 0,
4021            later_states: Vec::new(),
4022        }
4023    }
4024
4025    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4026        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4027    }
4028
4029    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4030        self.insert(path.index, path.consumed_eof, Self::EXITED)
4031    }
4032
4033    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4034        let Some(offset) = index.checked_sub(self.base_index) else {
4035            return false;
4036        };
4037        let state = if offset == 0 {
4038            &mut self.base_state
4039        } else {
4040            if self.later_states.len() < offset {
4041                self.later_states.resize(offset, 0);
4042            }
4043            &mut self.later_states[offset - 1]
4044        };
4045        let bit = 1 << (base_bit + u8::from(consumed_eof));
4046        let is_new = *state & bit == 0;
4047        *state |= bit;
4048        is_new
4049    }
4050}
4051
4052fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4053    if state.precedence_rule_decision()
4054        || !matches!(
4055            state.kind(),
4056            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4057        )
4058        || state.transitions().len() != 2
4059    {
4060        return None;
4061    }
4062    let mut enter = None;
4063    let mut exit = None;
4064    for (index, transition) in state.transitions().iter().enumerate() {
4065        if transition.kind() != ParserTransitionKind::Epsilon {
4066            return None;
4067        }
4068        let target = transition.target();
4069        if atn
4070            .state(target)
4071            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4072        {
4073            if exit.replace((index, target)).is_some() {
4074                return None;
4075            }
4076        } else if enter.replace((index, target)).is_some() {
4077            return None;
4078        }
4079    }
4080    let (enter_transition_index, enter_target) = enter?;
4081    let (exit_transition_index, exit_target) = exit?;
4082    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4083        atn.state(exit_target)?.loop_back_state()?
4084    } else {
4085        state.state_number()
4086    };
4087    Some(FastRepetitionShape {
4088        enter_target,
4089        exit_target,
4090        body_stop_state,
4091        enter_transition_index,
4092        exit_transition_index,
4093    })
4094}
4095
4096fn push_fast_repetition_work(
4097    work: &mut Vec<FastRepetitionWork>,
4098    shape: FastRepetitionShape,
4099    path: FastRepetitionPath,
4100    lookahead: Option<&DecisionLookahead>,
4101    symbol: i32,
4102) {
4103    // Match the normal recognizer's FIRST-set pruning before queueing work.
4104    // Ambiguous body paths still share the coordinate bitmap below.
4105    let transition_is_viable = |transition_index: usize| {
4106        let Some(entry) = lookahead else {
4107            return true;
4108        };
4109        let Some(transition) = entry.transitions.get(transition_index) else {
4110            return true;
4111        };
4112        transition.nullable || transition.symbols.contains(symbol)
4113    };
4114    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4115    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4116    if shape.enter_transition_index < shape.exit_transition_index {
4117        if exit_is_viable {
4118            work.push(FastRepetitionWork::Exit(path));
4119        }
4120        if enter_is_viable {
4121            work.push(FastRepetitionWork::Enter(path));
4122        }
4123    } else {
4124        if enter_is_viable {
4125            work.push(FastRepetitionWork::Enter(path));
4126        }
4127        if exit_is_viable {
4128            work.push(FastRepetitionWork::Exit(path));
4129        }
4130    }
4131}
4132
4133/// Memo key for the fast recognizer. `recovery_symbols` must come from
4134/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4135/// key, so equal sets share one allocation and the key can store that
4136/// allocation's address instead of cloning an `Rc` and walking the full
4137/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4138/// into distinct cache coordinates.
4139#[derive(Clone, Debug)]
4140struct FastRecognizeKey {
4141    state_number: usize,
4142    stop_state: usize,
4143    index: usize,
4144    rule_start_index: usize,
4145    decision_start_index: Option<usize>,
4146    precedence: i32,
4147    recovery_symbols_id: usize,
4148    recovery_state: Option<usize>,
4149}
4150
4151impl PartialEq for FastRecognizeKey {
4152    fn eq(&self, other: &Self) -> bool {
4153        if self.state_number != other.state_number
4154            || self.stop_state != other.stop_state
4155            || self.index != other.index
4156            || self.rule_start_index != other.rule_start_index
4157            || self.decision_start_index != other.decision_start_index
4158            || self.precedence != other.precedence
4159            || self.recovery_state != other.recovery_state
4160            || self.recovery_symbols_id != other.recovery_symbols_id
4161        {
4162            return false;
4163        }
4164        true
4165    }
4166}
4167
4168impl Eq for FastRecognizeKey {}
4169
4170impl Hash for FastRecognizeKey {
4171    fn hash<H: Hasher>(&self, hasher: &mut H) {
4172        self.state_number.hash(hasher);
4173        self.stop_state.hash(hasher);
4174        self.index.hash(hasher);
4175        self.rule_start_index.hash(hasher);
4176        self.decision_start_index.hash(hasher);
4177        self.precedence.hash(hasher);
4178        self.recovery_state.hash(hasher);
4179        self.recovery_symbols_id.hash(hasher);
4180    }
4181}
4182
4183struct FastRecoveryRequest<'a, 'b> {
4184    atn: &'a Atn,
4185    transition: ParserTransition<'a>,
4186    expected_symbols: Rc<BTreeSet<i32>>,
4187    target: usize,
4188    request: FastRecognizeRequest,
4189    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4190    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4191    expected: &'b mut ExpectedTokens,
4192}
4193
4194struct FastCurrentTokenDeletionRequest<'a, 'b> {
4195    atn: &'a Atn,
4196    expected_symbols: Rc<BTreeSet<i32>>,
4197    request: FastRecognizeRequest,
4198    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4199    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4200    expected: &'b mut ExpectedTokens,
4201}
4202
4203#[derive(Clone, Copy)]
4204struct FastChildRuleFailureRecoveryRequest<'a> {
4205    atn: &'a Atn,
4206    rule_index: usize,
4207    start_index: usize,
4208    follow_state: usize,
4209    stop_state: usize,
4210    expected: &'a ExpectedTokens,
4211}
4212
4213struct RecoveryRequest<'a, 'b> {
4214    atn: &'a Atn,
4215    transition: ParserTransition<'a>,
4216    expected_symbols: BTreeSet<i32>,
4217    target: usize,
4218    request: RecognizeRequest<'a>,
4219    visiting: &'b mut BTreeSet<RecognizeKey>,
4220    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4221    expected: &'b mut ExpectedTokens,
4222}
4223
4224struct CurrentTokenDeletionRequest<'a, 'b> {
4225    atn: &'a Atn,
4226    expected_symbols: BTreeSet<i32>,
4227    request: RecognizeRequest<'a>,
4228    visiting: &'b mut BTreeSet<RecognizeKey>,
4229    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4230    expected: &'b mut ExpectedTokens,
4231}
4232
4233/// Carries the state needed after the normal token-recovery strategies fail
4234/// for a consuming transition.
4235struct ConsumingFailureFallback<'a> {
4236    atn: &'a Atn,
4237    target: usize,
4238    request: RecognizeRequest<'a>,
4239    symbol: i32,
4240    expected_symbols: BTreeSet<i32>,
4241    decision_start_index: Option<usize>,
4242    decision: Option<usize>,
4243}
4244
4245/// Captures the parent-rule context needed when a called rule fails before it
4246/// can produce a normal outcome.
4247struct ChildRuleFailureRecovery<'a> {
4248    atn: &'a Atn,
4249    rule_index: usize,
4250    start_index: usize,
4251    follow_state: usize,
4252    stop_state: usize,
4253    member_values: BTreeMap<usize, i64>,
4254    expected: &'a ExpectedTokens,
4255}
4256
4257/// Bundles the context needed to evaluate one semantic predicate transition.
4258#[derive(Clone, Copy, Debug)]
4259struct PredicateEval<'a> {
4260    index: usize,
4261    rule_index: usize,
4262    pred_index: usize,
4263    predicates: &'a [(usize, usize, ParserPredicate)],
4264    semantics: Option<&'a ParserSemantics>,
4265    context: Option<&'a ParserRuleContext>,
4266    local_int_arg: Option<(usize, i64)>,
4267    member_values: &'a BTreeMap<usize, i64>,
4268}
4269
4270#[derive(Clone, Copy, Debug)]
4271struct ParserSemanticHookRequest<'a> {
4272    index: usize,
4273    rule_index: usize,
4274    pred_index: usize,
4275    context: Option<&'a ParserRuleContext>,
4276    local_int_arg: Option<(usize, i64)>,
4277    member_values: &'a BTreeMap<usize, i64>,
4278}
4279
4280/// Predicate-evaluation context over the recognizer's speculative state.
4281///
4282/// This sits in the prediction hot loop, so everything is borrowed: member
4283/// state read-only from the current speculative path and the rule name
4284/// straight from recognizer metadata. Predicates are pure by construction
4285/// ([`semir::PExpr`] has no mutating node); statement execution uses
4286/// [`ParserTableSemCtx`] (speculative member/return replay) and
4287/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4288struct ParserSemIrCtx<'a, S, H>
4289where
4290    S: TokenSource,
4291    H: SemanticHooks,
4292{
4293    input: &'a mut CommonTokenStream<S>,
4294    tree_storage: &'a ParseTreeStorage,
4295    semantic_hooks: &'a mut H,
4296    rule_index: usize,
4297    coordinate_index: usize,
4298    rule_name: Option<&'a str>,
4299    context: Option<&'a ParserRuleContext>,
4300    local_int_arg: Option<(usize, i64)>,
4301    member_values: &'a BTreeMap<usize, i64>,
4302    invoked_predicates: &'a mut Vec<(usize, usize)>,
4303    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4304    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4305    /// table path instead of coercing the miss to `false`.
4306    unknown_predicate_policy: UnknownSemanticPolicy,
4307    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4308}
4309
4310impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4311where
4312    S: TokenSource,
4313    H: SemanticHooks,
4314{
4315    type TokenText<'a>
4316        = TokenView<'a>
4317    where
4318        Self: 'a;
4319
4320    fn la(&mut self, offset: isize) -> i64 {
4321        i64::from(self.input.la(offset))
4322    }
4323
4324    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4325        self.input.lt(offset)
4326    }
4327
4328    fn token_index_adjacent(&mut self) -> bool {
4329        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4330            return false;
4331        };
4332        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4333            return false;
4334        };
4335        first + 1 == second
4336    }
4337
4338    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4339        self.context.and_then(|context| {
4340            context
4341                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4342                .next()
4343                .map(crate::tree::RuleNodeView::text)
4344        })
4345    }
4346
4347    fn member(&self, member: usize) -> Option<i64> {
4348        Some(self.member_values.get(&member).copied().unwrap_or_default())
4349    }
4350
4351    fn local_arg(&self) -> Option<i64> {
4352        self.local_int_arg.map(|(_, value)| value)
4353    }
4354
4355    fn column(&self) -> Option<i64> {
4356        None
4357    }
4358
4359    fn token_start_column(&self) -> Option<i64> {
4360        None
4361    }
4362
4363    fn token_text_so_far(&self) -> Option<String> {
4364        None
4365    }
4366
4367    fn hook(&mut self, _hook: HookId) -> bool {
4368        let mut ctx = ParserSemCtx {
4369            input: &mut *self.input,
4370            tree_storage: self.tree_storage,
4371            rule_index: self.rule_index,
4372            coordinate_index: self.coordinate_index,
4373            rule_name: self.rule_name.map(str::to_owned),
4374            context: self.context,
4375            tree: None,
4376            local_int_arg: self.local_int_arg,
4377            member_values: self.member_values,
4378            action: None,
4379        };
4380        match self
4381            .semantic_hooks
4382            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4383        {
4384            Some(result) => result,
4385            // No hook answered this coordinate: fall through to the configured
4386            // policy instead of silently rejecting the alternative, matching the
4387            // legacy table path's dispatch chain (hook → policy).
4388            None => apply_unknown_predicate_policy(
4389                self.unknown_predicate_policy,
4390                self.rule_index,
4391                self.coordinate_index,
4392                self.unknown_predicate_hits,
4393            ),
4394        }
4395    }
4396
4397    fn trace_bool(&mut self, value: bool) -> bool {
4398        let key = (self.rule_index, self.coordinate_index);
4399        if !self.invoked_predicates.contains(&key) {
4400            self.invoked_predicates.push(key);
4401            use std::io::Write as _;
4402            let mut stdout = std::io::stdout().lock();
4403            let _ = writeln!(stdout, "eval={value}");
4404        }
4405        value
4406    }
4407}
4408
4409/// Captures predicate-failure recovery metadata for fail-option predicates.
4410struct PredicateFailureRecovery<'a> {
4411    rule_index: usize,
4412    index: usize,
4413    message: &'a str,
4414    member_values: BTreeMap<usize, i64>,
4415    return_values: BTreeMap<String, i64>,
4416    rule_alt_number: usize,
4417}
4418
4419#[derive(Debug)]
4420enum DirectAdaptiveParseControl {
4421    Fallback(DirectAdaptiveFallback),
4422}
4423
4424#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4425enum DirectAdaptiveFallback {
4426    Action,
4427    InvalidAlt,
4428    LeftRecursiveBoundary,
4429    MissingAtn,
4430    NoTransition,
4431    Predicate,
4432    Prediction,
4433    Precedence,
4434    RuleStop,
4435    SemanticContext,
4436    StepLimit,
4437    TokenMismatch,
4438    UnknownDecision,
4439}
4440
4441type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4442
4443struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4444where
4445    S: TokenSource,
4446    H: SemanticHooks,
4447{
4448    parser: &'sim mut BaseParser<S, H>,
4449    atn: &'atn Atn,
4450    simulator: &'sim mut ParserAtnSimulator<'atn>,
4451    decision_by_state: Vec<Option<usize>>,
4452    steps: usize,
4453}
4454
4455/// Outcome of a generated token / set / not-set match that may recover.
4456///
4457/// Generated parsers append `children` to the current rule context. `consumed_eof`
4458/// reports whether the match actually consumed a real EOF terminal — it is true
4459/// only on a successful match (or single-token deletion that lands on EOF), and
4460/// always false on single-token insertion, which synthesizes a missing token and
4461/// consumes nothing. Generated code feeds this into `finish_rule`'s
4462/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4463/// truly matched, matching ANTLR's `matchedEOF` semantics.
4464#[derive(Clone, Debug, Eq, PartialEq)]
4465pub struct GeneratedMatch {
4466    children: GeneratedMatchChildren,
4467    consumed_eof: bool,
4468}
4469
4470#[derive(Clone, Debug, Eq, PartialEq)]
4471enum GeneratedMatchChildren {
4472    One(ParseTree),
4473    Many(Vec<ParseTree>),
4474}
4475
4476struct GeneratedMatchChildrenIntoIter {
4477    one: Option<ParseTree>,
4478    many: Option<std::vec::IntoIter<ParseTree>>,
4479}
4480
4481impl Iterator for GeneratedMatchChildrenIntoIter {
4482    type Item = ParseTree;
4483
4484    fn next(&mut self) -> Option<Self::Item> {
4485        self.one
4486            .take()
4487            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4488    }
4489}
4490
4491impl GeneratedMatch {
4492    /// Parse-tree children produced by the match (the matched terminal, an
4493    /// error node plus deleted-then-matched terminal, or a single missing-token
4494    /// error node).
4495    #[must_use]
4496    pub fn children(&self) -> &[ParseTree] {
4497        match &self.children {
4498            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4499            GeneratedMatchChildren::Many(children) => children,
4500        }
4501    }
4502
4503    /// Consumes the result, returning the children for appending to the rule
4504    /// context.
4505    #[must_use]
4506    pub fn into_children(self) -> Vec<ParseTree> {
4507        match self.children {
4508            GeneratedMatchChildren::One(child) => vec![child],
4509            GeneratedMatchChildren::Many(children) => children,
4510        }
4511    }
4512
4513    /// Consumes the match without allocating for the common single-child case.
4514    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4515        match self.children {
4516            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4517                one: Some(child),
4518                many: None,
4519            },
4520            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4521                one: None,
4522                many: Some(children.into_iter()),
4523            },
4524        }
4525    }
4526
4527    /// Whether a real EOF terminal was consumed by this match.
4528    #[must_use]
4529    pub const fn consumed_eof(&self) -> bool {
4530        self.consumed_eof
4531    }
4532}
4533
4534impl<S> BaseParser<S, NoSemanticHooks>
4535where
4536    S: TokenSource,
4537{
4538    /// Creates a parser base over a buffered token stream and recognizer
4539    /// metadata.
4540    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4541        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4542    }
4543}
4544
4545impl<S, H> BaseParser<S, H>
4546where
4547    S: TokenSource,
4548    H: SemanticHooks,
4549{
4550    /// Creates a parser base with caller-owned semantic hooks.
4551    pub fn with_semantic_hooks(
4552        input: CommonTokenStream<S>,
4553        data: RecognizerData,
4554        semantic_hooks: H,
4555    ) -> Self {
4556        Self {
4557            input,
4558            tree: ParseTreeStorage::new(),
4559            data,
4560            semantic_hooks,
4561            build_parse_trees: true,
4562            syntax_errors: 0,
4563            report_diagnostic_errors: false,
4564            prediction_mode: PredictionMode::Ll,
4565            prediction_diagnostics: Vec::new(),
4566            reported_prediction_diagnostics: BTreeSet::new(),
4567            generated_parser_diagnostics: Vec::new(),
4568            generated_sync_expected: None,
4569            int_members: BTreeMap::new(),
4570            rule_context_stack: Vec::new(),
4571            rule_context_version: 0,
4572            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4573            pending_invoking_states: Vec::new(),
4574            precedence_stack: vec![0],
4575            invoked_predicates: Vec::new(),
4576            bail_on_error: false,
4577            unknown_predicate_policy: UnknownSemanticPolicy::default(),
4578            unknown_predicate_hits: Vec::new(),
4579            unhandled_action_hits: Vec::new(),
4580            rule_first_set_cache: Vec::new(),
4581            state_expected_cache: FxHashMap::default(),
4582            state_expected_token_cache: FxHashMap::default(),
4583            rule_stop_reach_cache: Vec::new(),
4584            recovery_symbols_intern: FxHashMap::default(),
4585            decision_lookahead_cache: FxHashMap::default(),
4586            ll1_decision_cache: FxHashMap::default(),
4587            fast_predicate_cache: FxHashMap::default(),
4588            empty_cycle_cache: Vec::new(),
4589            single_outcome_memo_mode: SingleOutcomeMemoMode::Probe,
4590            single_outcome_probe_seen: FxHashSet::default(),
4591            single_outcome_probe_samples: 0,
4592            single_outcome_probe_repeats: 0,
4593            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
4594            empty_recovery_symbols: Rc::new(BTreeSet::new()),
4595            fast_first_set_prefilter: true,
4596            fast_recovery_enabled: true,
4597            fast_token_nodes_enabled: true,
4598            recognition_arena: RecognitionArena::default(),
4599            last_recognition_arena_root: NodeSeqId::EMPTY,
4600            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
4601        }
4602    }
4603
4604    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
4605        &mut self.input
4606    }
4607
4608    /// Installs the policy for predicate coordinates that no translated table
4609    /// entry or user hook resolves.
4610    ///
4611    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
4612    /// but generated recursive-descent rules evaluate predicates directly
4613    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
4614    /// going through those options. Generated parser constructors call this so
4615    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
4616    /// the field at its `AssumeTrue` default and silently accepting an
4617    /// unimplemented hook predicate.
4618    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
4619        self.unknown_predicate_policy = policy;
4620    }
4621
4622    /// Reports any unknown predicate coordinate the generated-direct path
4623    /// recorded under [`UnknownSemanticPolicy::Error`], as an
4624    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
4625    /// after a rule completes so the fail-loud policy surfaces on the
4626    /// generated path the same way the interpreter entry surfaces it.
4627    #[must_use]
4628    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
4629        let error = self.unknown_semantic_error();
4630        self.unknown_predicate_hits.clear();
4631        self.unhandled_action_hits.clear();
4632        error
4633    }
4634
4635    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
4636    ///
4637    /// Generated parsers call this at the true top-level entry so a parser
4638    /// reused after a fail-loud (or recovered) parse starts clean, without
4639    /// clearing hits mid-parse where a generated parent still needs a child's
4640    /// recorded coordinate to survive to the top-level boundary.
4641    pub fn reset_unknown_semantic_hits(&mut self) {
4642        self.unknown_predicate_hits.clear();
4643        self.unhandled_action_hits.clear();
4644    }
4645
4646    /// Returns the token stream owned by this parser.
4647    #[must_use]
4648    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
4649        &self.input
4650    }
4651
4652    /// Returns the canonical token store referenced by parse trees.
4653    #[must_use]
4654    pub const fn token_store(&self) -> &TokenStore {
4655        self.input.token_store()
4656    }
4657
4658    /// Returns the flat CST storage populated by completed rules.
4659    #[must_use]
4660    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
4661        &self.tree
4662    }
4663
4664    /// Resolves a compact parse-tree ID into a borrowing node view.
4665    #[must_use]
4666    pub fn node(&self, id: NodeId) -> Node<'_> {
4667        self.tree
4668            .node(self.input.token_store(), id)
4669            .expect("parser-produced node ID should remain valid")
4670    }
4671
4672    /// Consumes this parser and returns its token stream.
4673    #[must_use]
4674    pub fn into_token_stream(self) -> CommonTokenStream<S> {
4675        self.input
4676    }
4677
4678    /// Consumes this parser and returns its canonical token store.
4679    #[must_use]
4680    pub fn into_token_store(self) -> TokenStore {
4681        self.input.into_token_store()
4682    }
4683
4684    /// Consumes the parser and pairs its token store and flat CST with `root`.
4685    #[must_use]
4686    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
4687        ParsedFile::new(self.input.into_token_store(), self.tree, root)
4688    }
4689
4690    /// Returns the number of parser syntax errors recorded by committed parse
4691    /// paths so far.
4692    pub const fn number_of_syntax_errors(&self) -> usize {
4693        self.syntax_errors
4694    }
4695
4696    /// Computes reachability and retained-capacity counters for the most recent
4697    /// interpreted-rule recognition arena.
4698    ///
4699    /// The reachability scan is linear in the arena size and is deferred until
4700    /// this instrumentation method is called.
4701    #[must_use]
4702    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
4703        self.recognition_arena.stats(
4704            self.last_recognition_arena_root,
4705            self.last_recognition_arena_diagnostics,
4706        )
4707    }
4708
4709    /// Records a syntax error that generated parser code returns as fatal before
4710    /// it can recover into the current rule context.
4711    pub const fn record_generated_syntax_error(&mut self) {
4712        self.record_syntax_errors(1);
4713    }
4714
4715    const fn record_syntax_errors(&mut self, count: usize) {
4716        self.syntax_errors = self.syntax_errors.saturating_add(count);
4717    }
4718
4719    /// Emits diagnostics buffered by the token stream while generated parser
4720    /// code was fetching lexer tokens directly.
4721    pub fn report_token_source_errors(&mut self) {
4722        report_token_source_errors(&self.input.drain_source_errors());
4723    }
4724
4725    /// Captures generated-parser diagnostics and syntax-error count before a
4726    /// speculative generated rule path.
4727    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
4728        GeneratedDiagnosticsCheckpoint {
4729            diagnostics_len: self.generated_parser_diagnostics.len(),
4730            syntax_errors: self.syntax_errors,
4731            tree: self.tree.checkpoint(),
4732        }
4733    }
4734
4735    /// Restores generated-parser diagnostics after a speculative rule path failed.
4736    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
4737        self.generated_parser_diagnostics
4738            .truncate(marker.diagnostics_len);
4739        self.syntax_errors = marker.syntax_errors;
4740        self.generated_sync_expected = None;
4741        self.tree.rollback(marker.tree);
4742    }
4743
4744    /// Emits diagnostics recorded by committed generated parser recovery.
4745    pub fn report_generated_parser_diagnostics(&mut self) {
4746        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
4747        let token_errors = self.input.drain_source_errors();
4748        report_generated_diagnostics(&parser_diagnostics, &token_errors);
4749    }
4750
4751    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
4752    /// decision code.
4753    pub fn record_generated_ambiguity_diagnostic(
4754        &mut self,
4755        atn: &Atn,
4756        state_number: usize,
4757        start_index: usize,
4758        stop_index: usize,
4759        alts: &[usize],
4760    ) {
4761        if !self.report_diagnostic_errors || alts.len() < 2 {
4762            return;
4763        }
4764        let Some(decision) = atn
4765            .decision_to_state()
4766            .iter()
4767            .position(|candidate| candidate == state_number)
4768        else {
4769            return;
4770        };
4771        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
4772            return;
4773        };
4774        let rule_name = self
4775            .rule_names()
4776            .get(rule_index)
4777            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
4778        let input = display_input_text(&self.input.text(start_index, stop_index));
4779        let alts = alts
4780            .iter()
4781            .map(usize::to_string)
4782            .collect::<Vec<_>>()
4783            .join(", ");
4784        let key = (decision, start_index, format!("{alts}:{input}"));
4785        if !self.reported_prediction_diagnostics.insert(key) {
4786            return;
4787        }
4788        let start_diagnostic = diagnostic_for_token(
4789            self.token_at(start_index),
4790            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
4791        );
4792        let stop_diagnostic = diagnostic_for_token(
4793            self.token_at(stop_index),
4794            format!(
4795                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
4796            ),
4797        );
4798        self.generated_parser_diagnostics.push(start_diagnostic);
4799        self.generated_parser_diagnostics.push(stop_diagnostic);
4800    }
4801
4802    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
4803    /// parser calls to the adaptive simulator.
4804    pub fn record_generated_prediction_diagnostic(
4805        &mut self,
4806        atn: &Atn,
4807        state_number: usize,
4808        prediction: &ParserAtnPrediction,
4809    ) {
4810        let Some(diagnostic) = &prediction.diagnostic else {
4811            return;
4812        };
4813        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
4814            return;
4815        }
4816        let Some(decision) = atn
4817            .decision_to_state()
4818            .iter()
4819            .position(|candidate| candidate == state_number)
4820        else {
4821            return;
4822        };
4823        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
4824            return;
4825        };
4826        let rule_name = self
4827            .rule_names()
4828            .get(rule_index)
4829            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
4830        let attempt_input = display_input_text(
4831            &self
4832                .input
4833                .text(diagnostic.start_index, diagnostic.sll_stop_index),
4834        );
4835        let result_input = display_input_text(
4836            &self
4837                .input
4838                .text(diagnostic.start_index, diagnostic.ll_stop_index),
4839        );
4840        let alts = diagnostic
4841            .conflicting_alts
4842            .iter()
4843            .map(usize::to_string)
4844            .collect::<Vec<_>>()
4845            .join(", ");
4846        let key = (
4847            decision,
4848            diagnostic.start_index,
4849            format!(
4850                "{:?}:{alts}:{attempt_input}:{result_input}",
4851                diagnostic.kind
4852            ),
4853        );
4854        if !self.reported_prediction_diagnostics.insert(key) {
4855            return;
4856        }
4857        let attempt_diagnostic = diagnostic_for_token(
4858            self.token_at(diagnostic.sll_stop_index),
4859            format!(
4860                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
4861            ),
4862        );
4863        self.generated_parser_diagnostics.push(attempt_diagnostic);
4864        let message = match diagnostic.kind {
4865            ParserAtnPredictionDiagnosticKind::Ambiguity => {
4866                // Java's DiagnosticErrorListener is exactOnly by default:
4867                // non-exact ambiguities (default LL mode stopping at the
4868                // first resolvable conflict) report the attempt above but
4869                // suppress the ambiguity line itself.
4870                if !diagnostic.exact {
4871                    return;
4872                }
4873                format!(
4874                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
4875                )
4876            }
4877            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
4878                format!(
4879                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
4880                )
4881            }
4882        };
4883        let result_diagnostic =
4884            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
4885        self.generated_parser_diagnostics.push(result_diagnostic);
4886    }
4887
4888    pub fn la(&self, offset: isize) -> i32 {
4889        self.input.la_token(offset)
4890    }
4891
4892    pub fn consume(&mut self) {
4893        IntStream::consume(&mut self.input);
4894    }
4895
4896    /// Sets a generated integer member value used by target-template tests.
4897    pub fn set_int_member(&mut self, member: usize, value: i64) {
4898        self.int_members.insert(member, value);
4899    }
4900
4901    /// Reads a generated integer member value.
4902    pub fn int_member(&self, member: usize) -> Option<i64> {
4903        self.int_members.get(&member).copied()
4904    }
4905
4906    /// Captures generated integer members before speculative generated parser
4907    /// execution.
4908    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
4909        self.int_members.clone()
4910    }
4911
4912    /// Restores generated integer members after generated parser fallback.
4913    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
4914        self.int_members = members;
4915    }
4916
4917    /// Adds `delta` to a generated integer member and returns the new value.
4918    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
4919        let value = self.int_members.entry(member).or_default();
4920        *value += delta;
4921        *value
4922    }
4923
4924    fn token_type_for_id(&self, id: TokenId) -> i32 {
4925        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
4926    }
4927
4928    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
4929        if self.build_parse_trees {
4930            self.tree.terminal(id)
4931        } else {
4932            NodeId::placeholder()
4933        }
4934    }
4935
4936    fn error_tree(&mut self, id: TokenId) -> ParseTree {
4937        if self.build_parse_trees {
4938            self.tree.error(id)
4939        } else {
4940            NodeId::placeholder()
4941        }
4942    }
4943
4944    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
4945        context.set_start_id(id);
4946    }
4947
4948    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
4949        context.set_stop_id(id);
4950    }
4951
4952    fn insert_synthetic_token(
4953        &mut self,
4954        token_type: i32,
4955        text: String,
4956        line: usize,
4957        column: usize,
4958    ) -> Result<TokenId, AntlrError> {
4959        self.input
4960            .insert(
4961                TokenSpec::explicit(token_type, text)
4962                    .with_span(usize::MAX, usize::MAX)
4963                    .with_byte_span(0, 0)
4964                    .with_position(line, column),
4965            )
4966            .map_err(|error| AntlrError::Unsupported(error.to_string()))
4967    }
4968
4969    /// Matches and consumes the current token when it has the expected token
4970    /// type.
4971    ///
4972    /// On success the consumed token is wrapped as a terminal parse-tree node.
4973    /// On mismatch the error carries vocabulary display names so diagnostics are
4974    /// stable across literal and symbolic token naming.
4975    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
4976        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
4977            line: 0,
4978            column: 0,
4979            message: "missing current token".to_owned(),
4980        })?;
4981        let current_type = self.token_type_for_id(current);
4982        if current_type == token_type {
4983            self.consume();
4984            Ok(self.terminal_tree(current))
4985        } else {
4986            Err(AntlrError::MismatchedInput {
4987                expected: self.vocabulary().display_name(token_type),
4988                found: self.vocabulary().display_name(current_type),
4989            })
4990        }
4991    }
4992
4993    /// Matches a token from generated recursive-descent code, including ANTLR's
4994    /// single-token insertion recovery when the active rule context can legally
4995    /// continue at the current input symbol.
4996    pub fn match_token_recovering(
4997        &mut self,
4998        token_type: i32,
4999        follow_state: usize,
5000        atn: &Atn,
5001    ) -> Result<GeneratedMatch, AntlrError> {
5002        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5003            line: 0,
5004            column: 0,
5005            message: "missing current token".to_owned(),
5006        })?;
5007        let current_type = self.token_type_for_id(current);
5008        if current_type == token_type {
5009            self.generated_sync_expected = None;
5010            let consumed_eof = current_type == TOKEN_EOF;
5011            self.consume();
5012            return Ok(GeneratedMatch {
5013                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5014                consumed_eof,
5015            });
5016        }
5017        let mut expected_symbols = BTreeSet::new();
5018        expected_symbols.insert(token_type);
5019        self.recover_generated_match(current, &expected_symbols, follow_state, atn, |symbol| {
5020            symbol == token_type
5021        })
5022    }
5023
5024    pub fn match_set_recovering(
5025        &mut self,
5026        intervals: &[(i32, i32)],
5027        follow_state: usize,
5028        atn: &Atn,
5029    ) -> Result<GeneratedMatch, AntlrError> {
5030        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5031            line: 0,
5032            column: 0,
5033            message: "missing current token".to_owned(),
5034        })?;
5035        let current_type = self.token_type_for_id(current);
5036        if interval_set_contains(intervals, current_type) {
5037            self.generated_sync_expected = None;
5038            let consumed_eof = current_type == TOKEN_EOF;
5039            self.consume();
5040            return Ok(GeneratedMatch {
5041                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5042                consumed_eof,
5043            });
5044        }
5045        let expected_symbols = interval_symbols(intervals);
5046        self.recover_generated_match(current, &expected_symbols, follow_state, atn, |symbol| {
5047            interval_set_contains(intervals, symbol)
5048        })
5049    }
5050
5051    pub fn match_not_set_recovering(
5052        &mut self,
5053        intervals: &[(i32, i32)],
5054        min_vocabulary: i32,
5055        max_vocabulary: i32,
5056        follow_state: usize,
5057        atn: &Atn,
5058    ) -> Result<GeneratedMatch, AntlrError> {
5059        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5060            line: 0,
5061            column: 0,
5062            message: "missing current token".to_owned(),
5063        })?;
5064        let current_type = self.token_type_for_id(current);
5065        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5066            && !interval_set_contains(intervals, current_type)
5067        {
5068            self.generated_sync_expected = None;
5069            let consumed_eof = current_type == TOKEN_EOF;
5070            self.consume();
5071            return Ok(GeneratedMatch {
5072                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5073                consumed_eof,
5074            });
5075        }
5076        let expected_symbols =
5077            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5078        self.recover_generated_match(current, &expected_symbols, follow_state, atn, |symbol| {
5079            (min_vocabulary..=max_vocabulary).contains(&symbol)
5080                && !interval_set_contains(intervals, symbol)
5081        })
5082    }
5083
5084    fn recover_generated_match(
5085        &mut self,
5086        current: TokenId,
5087        expected_symbols: &BTreeSet<i32>,
5088        follow_state: usize,
5089        atn: &Atn,
5090        matches: impl Fn(i32) -> bool,
5091    ) -> Result<GeneratedMatch, AntlrError> {
5092        let expected_display = self.expected_symbols_display(expected_symbols);
5093        let (current_type, current_line, current_column, current_display) = {
5094            let token = self
5095                .input
5096                .token_view(current)
5097                .expect("current token ID should be valid");
5098            (
5099                token.token_type(),
5100                token.line(),
5101                token.column(),
5102                token_input_display(&token),
5103            )
5104        };
5105        if self.bail_on_error {
5106            return Err(AntlrError::ParserError {
5107                line: current_line,
5108                column: current_column,
5109                message: format!("mismatched input {current_display} expecting {expected_display}"),
5110            });
5111        }
5112        if current_type != TOKEN_EOF
5113            && let Some(next) = self.input.lt_id(2)
5114            && matches(self.token_type_for_id(next))
5115        {
5116            let message =
5117                format!("extraneous input {current_display} expecting {expected_display}");
5118            self.push_generated_parser_diagnostic(ParserDiagnostic {
5119                line: current_line,
5120                column: current_column,
5121                message,
5122            });
5123            self.record_syntax_errors(1);
5124            self.generated_sync_expected = None;
5125            // Single-token deletion: skip `current`, then accept `next`. The
5126            // accepted token can be EOF only if it is a real EOF terminal.
5127            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5128            self.consume();
5129            self.consume();
5130            return Ok(GeneratedMatch {
5131                children: GeneratedMatchChildren::Many(vec![
5132                    self.error_tree(current),
5133                    self.terminal_tree(next),
5134                ]),
5135                consumed_eof,
5136            });
5137        }
5138        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5139        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5140        // *after* the current element can consume the current symbol. At EOF that
5141        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5142        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5143        // when EOF merely leaks in from the empty enclosing context (as in
5144        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5145        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5146        // so consult the follow state's OWN expected set for the explicit case.
5147        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5148            && self
5149                .cached_state_expected_symbols(atn, follow_state)
5150                .contains(&TOKEN_EOF);
5151        if follow_symbols.contains(&current_type)
5152            && (current_type != TOKEN_EOF
5153                || self.rule_context_stack.len() > 1
5154                || expected_symbols.is_empty()
5155                || follow_explicitly_expects_eof)
5156        {
5157            let message = format!("missing {expected_display} at {current_display}");
5158            self.push_generated_parser_diagnostic(ParserDiagnostic {
5159                line: current_line,
5160                column: current_column,
5161                message,
5162            });
5163            self.record_syntax_errors(1);
5164            self.generated_sync_expected = None;
5165            let token_type = expected_symbols.iter().next().copied().unwrap_or(TOKEN_EOF);
5166            let mut missing_symbol = BTreeSet::new();
5167            missing_symbol.insert(token_type);
5168            let missing_display = self.expected_symbols_display(&missing_symbol);
5169            let token = self.insert_synthetic_token(
5170                token_type,
5171                format!("<missing {missing_display}>"),
5172                current_line,
5173                current_column,
5174            )?;
5175            // Single-token insertion synthesizes a missing token and consumes
5176            // nothing, so no EOF terminal is consumed even when the lookahead is
5177            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5178            // from recording EOF as the rule stop on this recovery path.
5179            return Ok(GeneratedMatch {
5180                children: GeneratedMatchChildren::One(self.error_tree(token)),
5181                consumed_eof: false,
5182            });
5183        }
5184        let mismatch_expected = self.generated_sync_expected.take().map_or_else(
5185            || expected_symbols.clone(),
5186            |symbols| symbols.to_btree_set(),
5187        );
5188        let mismatch_expected_display = self.expected_symbols_display(&mismatch_expected);
5189        Err(AntlrError::ParserError {
5190            line: current_line,
5191            column: current_column,
5192            message: format!(
5193                "mismatched input {current_display} expecting {mismatch_expected_display}"
5194            ),
5195        })
5196    }
5197
5198    fn generated_recovery_follow_symbols(
5199        &mut self,
5200        atn: &Atn,
5201        follow_state: usize,
5202    ) -> BTreeSet<i32> {
5203        let mut follow = self
5204            .cached_state_expected_symbols(atn, follow_state)
5205            .as_ref()
5206            .clone();
5207        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5208            follow.extend(self.context_expected_symbols(atn));
5209        }
5210        follow
5211    }
5212
5213    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5214        self.match_token(TOKEN_EOF)
5215    }
5216
5217    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5218        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5219    }
5220
5221    pub fn match_not_set(
5222        &mut self,
5223        intervals: &[(i32, i32)],
5224        min_vocabulary: i32,
5225        max_vocabulary: i32,
5226    ) -> Result<ParseTree, AntlrError> {
5227        self.match_interval_condition(intervals, |symbol| {
5228            (min_vocabulary..=max_vocabulary).contains(&symbol)
5229                && !interval_set_contains(intervals, symbol)
5230        })
5231    }
5232
5233    fn match_interval_condition(
5234        &mut self,
5235        intervals: &[(i32, i32)],
5236        matches: impl FnOnce(i32) -> bool,
5237    ) -> Result<ParseTree, AntlrError> {
5238        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5239            line: 0,
5240            column: 0,
5241            message: "missing current token".to_owned(),
5242        })?;
5243        let current_type = self.token_type_for_id(current);
5244        if matches(current_type) {
5245            self.consume();
5246            Ok(self.terminal_tree(current))
5247        } else {
5248            Err(AntlrError::MismatchedInput {
5249                expected: self.interval_display(intervals),
5250                found: self.vocabulary().display_name(current_type),
5251            })
5252        }
5253    }
5254
5255    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5256        let values = intervals
5257            .iter()
5258            .map(|(start, stop)| {
5259                if start == stop {
5260                    self.vocabulary().display_name(*start)
5261                } else {
5262                    format!(
5263                        "{}..{}",
5264                        self.vocabulary().display_name(*start),
5265                        self.vocabulary().display_name(*stop)
5266                    )
5267                }
5268            })
5269            .collect::<Vec<_>>()
5270            .join(", ");
5271        format!("{{{values}}}")
5272    }
5273
5274    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5275        if self.build_parse_trees {
5276            self.tree.finish_rule(context)
5277        } else {
5278            NodeId::placeholder()
5279        }
5280    }
5281
5282    /// Enters a generated parser rule and returns the context object the
5283    /// generated method should populate.
5284    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
5285        self.set_state(state);
5286        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
5287        self.rule_context_stack.push(RuleContextFrame {
5288            rule_index,
5289            invoking_state,
5290        });
5291        self.advance_rule_context_version();
5292        let start_index = self.current_visible_index();
5293        let mut context = ParserRuleContext::new(rule_index, invoking_state);
5294        if let Some(token) = self.token_id_at(start_index) {
5295            self.set_context_start(&mut context, token);
5296        }
5297        context
5298    }
5299
5300    /// Records the ATN source state for the next generated rule invocation.
5301    ///
5302    /// ANTLR's full-context prediction reconstructs caller follow states from
5303    /// each active rule context's invoking state. Generated Rust rule methods are
5304    /// plain functions, so the caller supplies that ATN state just before making a
5305    /// rule call; `enter_rule` consumes it when the callee starts.
5306    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
5307        let marker = self.pending_invoking_states.len();
5308        self.pending_invoking_states.push(invoking_state);
5309        marker
5310    }
5311
5312    /// Discards an invoking-state marker if the callee did not consume it.
5313    pub fn discard_invoking_state(&mut self, marker: usize) {
5314        self.pending_invoking_states.truncate(marker);
5315    }
5316
5317    /// Exits the current generated parser rule.
5318    pub fn exit_rule(&mut self) {
5319        self.rule_context_stack.pop();
5320        self.advance_rule_context_version();
5321    }
5322
5323    /// Returns caller follow states for interning in a parser ATN simulator's
5324    /// prediction store. States are yielded outermost to innermost.
5325    pub fn prediction_context_return_states<'a>(
5326        &'a self,
5327        atn: &'a Atn,
5328    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
5329        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
5330            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
5331                return None;
5332            };
5333            let Some(Transition::Rule { follow_state, .. }) = atn
5334                .state(state_number)
5335                .and_then(|state| state.transitions().first())
5336                .map(ParserTransition::data)
5337            else {
5338                return None;
5339            };
5340            Some(follow_state)
5341        })
5342    }
5343
5344    /// Returns a generation that changes whenever the active rule stack changes.
5345    ///
5346    /// A parser ATN simulator uses this to reuse an interned outer prediction
5347    /// context while generated predictions remain in the same rule context.
5348    pub const fn rule_context_version(&self) -> usize {
5349        self.rule_context_version
5350    }
5351
5352    const fn advance_rule_context_version(&mut self) {
5353        self.rule_context_version = self.rule_context_version.wrapping_add(1);
5354    }
5355
5356    /// Adds a generated parser child only when parse-tree construction is
5357    /// enabled. The match is recorded on the context either way (via `add_child`,
5358    /// or `note_matched_child` when trees are off) so generated recovery can tell
5359    /// whether the rule has matched anything yet without depending on `children`.
5360    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
5361        if self.build_parse_trees {
5362            self.tree.add_child(context, child);
5363        } else {
5364            context.note_matched_child();
5365        }
5366    }
5367
5368    fn release_tree_scratch_if_idle(&mut self) {
5369        if self.rule_context_stack.is_empty() {
5370            self.tree.release_scratch();
5371        }
5372    }
5373
5374    /// Finishes a generated parser rule and returns its parse-tree node.
5375    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
5376        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5377        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5378            self.set_context_stop(&mut context, token);
5379        }
5380        let node = self.rule_node(context);
5381        self.exit_rule();
5382        self.release_tree_scratch_if_idle();
5383        node
5384    }
5385
5386    /// Recovers a generated rule catch block after a committed mismatch.
5387    ///
5388    /// ANTLR's generated parsers catch recognition errors inside each rule,
5389    /// report the original error, then consume unexpected tokens until the
5390    /// caller's recovery set can resume. Tokens consumed during recovery become
5391    /// error nodes in the current rule context.
5392    pub fn recover_generated_rule(
5393        &mut self,
5394        context: &mut ParserRuleContext,
5395        atn: &Atn,
5396        error: AntlrError,
5397    ) {
5398        let diagnostic = self.generated_rule_error_diagnostic(error);
5399        self.push_generated_parser_diagnostic(diagnostic);
5400        self.generated_sync_expected = None;
5401        let recovery_symbols = self.context_expected_symbols(atn);
5402        loop {
5403            let symbol = self.la(1);
5404            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
5405                break;
5406            }
5407            let Some(token) = self.input.lt_id(1) else {
5408                break;
5409            };
5410            self.consume();
5411            let child = self.error_tree(token);
5412            self.add_parse_child(context, child);
5413        }
5414        self.record_syntax_errors(1);
5415    }
5416
5417    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
5418        if self
5419            .generated_parser_diagnostics
5420            .iter()
5421            .any(|existing| existing == &diagnostic)
5422        {
5423            return;
5424        }
5425        self.generated_parser_diagnostics.push(diagnostic);
5426    }
5427
5428    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
5429        match error {
5430            AntlrError::ParserError {
5431                line,
5432                column,
5433                message,
5434            } => ParserDiagnostic {
5435                line,
5436                column,
5437                message,
5438            },
5439            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
5440                self.input.lt(1),
5441                format!("mismatched input {found} expecting {expected}"),
5442            ),
5443            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
5444                self.input.lt(1),
5445                format!("no viable alternative at input {input}"),
5446            ),
5447            AntlrError::LexerError {
5448                line,
5449                column,
5450                message,
5451            } => ParserDiagnostic {
5452                line,
5453                column,
5454                message,
5455            },
5456            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
5457        }
5458    }
5459
5460    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
5461    pub fn finish_recursion_rule(
5462        &mut self,
5463        mut context: ParserRuleContext,
5464        consumed_eof: bool,
5465    ) -> ParseTree {
5466        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5467        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5468            self.set_context_stop(&mut context, token);
5469        }
5470        let node = self.rule_node(context);
5471        self.unroll_recursion_context();
5472        self.release_tree_scratch_if_idle();
5473        node
5474    }
5475
5476    /// Enters a generated left-recursive rule at `precedence`.
5477    pub fn enter_recursion_rule(
5478        &mut self,
5479        state: isize,
5480        rule_index: usize,
5481        precedence: i32,
5482    ) -> ParserRuleContext {
5483        self.precedence_stack.push(precedence);
5484        self.enter_rule(state, rule_index)
5485    }
5486
5487    /// Replaces the current context while expanding a left-recursive rule.
5488    pub fn push_new_recursion_context(
5489        &mut self,
5490        state: isize,
5491        rule_index: usize,
5492    ) -> ParserRuleContext {
5493        self.set_state(state);
5494        ParserRuleContext::new(rule_index, state)
5495    }
5496
5497    /// Wraps the previous left-recursive context before parsing the next
5498    /// recursive operator alternative.
5499    pub fn push_new_recursion_context_with_previous(
5500        &mut self,
5501        state: isize,
5502        rule_index: usize,
5503        current: &mut ParserRuleContext,
5504    ) {
5505        self.set_state(state);
5506        if let Some(stop) = self
5507            .rule_stop_token_index(self.input.index(), false)
5508            .and_then(|index| self.token_id_at(index))
5509        {
5510            self.set_context_stop(current, stop);
5511        }
5512        let invoking_state = current.invoking_state();
5513        let start = current.start_id();
5514        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
5515        if start.is_some() {
5516            replacement.set_start_from_context(current);
5517        }
5518        let previous = std::mem::replace(current, replacement);
5519        if self.build_parse_trees {
5520            let previous = self.rule_node(previous);
5521            self.tree.add_child(current, previous);
5522        }
5523    }
5524
5525    /// Leaves a generated left-recursive rule.
5526    pub fn unroll_recursion_context(&mut self) {
5527        if self.precedence_stack.len() > 1 {
5528            self.precedence_stack.pop();
5529        }
5530        self.exit_rule();
5531    }
5532
5533    /// Predicts a generated left-recursive loop from one-token lookahead.
5534    ///
5535    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
5536    /// `None` means caller overlap, a dangerous multi-token prefix, or an
5537    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
5538    /// prediction (which includes the exit alt and precedence filtering).
5539    ///
5540    /// Single-token operators and multi-token prefixes that do not shadow a
5541    /// lower-precedence single-token operator keep the one-token enter fast path.
5542    ///
5543    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
5544    /// operator must not force enter; the adaptive decision may need to select
5545    /// the loop exit instead.
5546    pub fn left_recursive_loop_enter_prediction(
5547        &mut self,
5548        atn: &Atn,
5549        state_number: usize,
5550        precedence: i32,
5551    ) -> Option<bool> {
5552        let symbol = self.la(1);
5553        if symbol == TOKEN_EOF {
5554            return Some(false);
5555        }
5556        let operator_lookahead =
5557            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
5558        let can_single = operator_lookahead.single_token.contains(symbol);
5559        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
5560        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
5561        if !can_single && !can_multi && !can_predicate {
5562            return Some(false);
5563        }
5564        if can_predicate && !can_single {
5565            return None;
5566        }
5567        // Multi-token-only at this precedence, but the same symbol is a
5568        // single-token operator at precedence 0: defer so exit can win when the
5569        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
5570        if !can_single && can_multi && precedence > 0 {
5571            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
5572            if baseline.single_token.contains(symbol) {
5573                return None;
5574            }
5575        }
5576        let atn_key = SharedAtnCacheKey::for_atn(atn);
5577        let cached_overlap = self
5578            .left_recursive_caller_overlap_cache
5579            .iter()
5580            .flatten()
5581            .find(|entry| {
5582                entry.atn_key == atn_key
5583                    && entry.state_number == state_number
5584                    && entry.symbol == symbol
5585                    && entry.context_version == self.rule_context_version
5586            })
5587            .map(|entry| entry.overlaps);
5588        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
5589            let overlaps = caller_context_can_match_symbol_before_state(
5590                atn,
5591                self.prediction_context_return_states(atn),
5592                state_number,
5593                symbol,
5594            );
5595            if let Some(slot) = self
5596                .left_recursive_caller_overlap_cache
5597                .iter_mut()
5598                .find(|slot| slot.is_none())
5599            {
5600                *slot = Some(LeftRecursiveCallerOverlap {
5601                    atn_key,
5602                    state_number,
5603                    symbol,
5604                    context_version: self.rule_context_version,
5605                    overlaps,
5606                });
5607            }
5608            overlaps
5609        });
5610        if caller_overlaps {
5611            return None;
5612        }
5613        Some(true)
5614    }
5615
5616    fn cached_left_recursive_operator_lookahead(
5617        atn: &Atn,
5618        state_number: usize,
5619        precedence: i32,
5620    ) -> Rc<LeftRecursiveOperatorLookahead> {
5621        with_shared_atn_caches(atn, |cache| {
5622            let key = (state_number, precedence);
5623            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
5624                return Rc::clone(cached);
5625            }
5626            let lookahead = Rc::new(left_recursive_operator_lookahead(
5627                atn,
5628                state_number,
5629                precedence,
5630            ));
5631            cache
5632                .left_recursive_operator_lookahead
5633                .insert(key, Rc::clone(&lookahead));
5634            lookahead
5635        })
5636    }
5637
5638    /// Checks whether a generated left-recursive loop can unambiguously enter
5639    /// its operator alternative from one-token lookahead.
5640    pub fn left_recursive_loop_enter_matches(
5641        &mut self,
5642        atn: &Atn,
5643        state_number: usize,
5644        precedence: i32,
5645    ) -> bool {
5646        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
5647    }
5648
5649    /// Implements generated `precpred(_ctx, k)` checks.
5650    pub fn precpred(&self, precedence: i32) -> bool {
5651        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
5652    }
5653
5654    /// Evaluates a generated parser semantic predicate at the current input
5655    /// position.
5656    pub fn parser_semantic_predicate_matches(
5657        &mut self,
5658        predicates: &[(usize, usize, ParserPredicate)],
5659        rule_index: usize,
5660        pred_index: usize,
5661    ) -> bool {
5662        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
5663    }
5664
5665    /// Evaluates a generated parser semantic predicate with the current integer
5666    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
5667    pub fn parser_semantic_predicate_matches_with_local(
5668        &mut self,
5669        predicates: &[(usize, usize, ParserPredicate)],
5670        rule_index: usize,
5671        pred_index: usize,
5672        local_int_arg: i32,
5673    ) -> bool {
5674        self.parser_semantic_predicate_matches_inner(
5675            predicates,
5676            rule_index,
5677            pred_index,
5678            Some((rule_index, i64::from(local_int_arg))),
5679        )
5680    }
5681
5682    fn parser_semantic_predicate_matches_inner(
5683        &mut self,
5684        predicates: &[(usize, usize, ParserPredicate)],
5685        rule_index: usize,
5686        pred_index: usize,
5687        local_int_arg: Option<(usize, i64)>,
5688    ) -> bool {
5689        let index = self.input.index();
5690        let member_values = self.int_members.clone();
5691        self.parser_predicate_matches(PredicateEval {
5692            index,
5693            rule_index,
5694            pred_index,
5695            predicates,
5696            semantics: None,
5697            context: None,
5698            local_int_arg,
5699            member_values: &member_values,
5700        })
5701    }
5702
5703    /// Evaluates a generated parser semantic predicate with access to the
5704    /// current generated rule context.
5705    pub fn parser_semantic_predicate_matches_with_context_and_local(
5706        &mut self,
5707        predicates: &[(usize, usize, ParserPredicate)],
5708        rule_index: usize,
5709        pred_index: usize,
5710        context: &ParserRuleContext,
5711        local_int_arg: i32,
5712    ) -> bool {
5713        let index = self.input.index();
5714        let member_values = self.int_members.clone();
5715        self.parser_predicate_matches(PredicateEval {
5716            index,
5717            rule_index,
5718            pred_index,
5719            predicates,
5720            semantics: None,
5721            context: Some(context),
5722            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
5723            member_values: &member_values,
5724        })
5725    }
5726
5727    /// Evaluates a generated `SemIR` parser predicate with access to the current
5728    /// generated rule context.
5729    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
5730        &mut self,
5731        semantics: &ParserSemantics,
5732        rule_index: usize,
5733        pred_index: usize,
5734        context: &ParserRuleContext,
5735        local_int_arg: i32,
5736    ) -> bool {
5737        let index = self.input.index();
5738        let member_values = self.int_members.clone();
5739        self.parser_predicate_matches(PredicateEval {
5740            index,
5741            rule_index,
5742            pred_index,
5743            predicates: &[],
5744            semantics: Some(semantics),
5745            context: Some(context),
5746            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
5747            member_values: &member_values,
5748        })
5749    }
5750
5751    /// Returns a generated fail-option message for a parser semantic
5752    /// predicate coordinate.
5753    pub fn parser_semantic_predicate_failure_message(
5754        &self,
5755        rule_index: usize,
5756        pred_index: usize,
5757        predicates: &[(usize, usize, ParserPredicate)],
5758    ) -> Option<&'static str> {
5759        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
5760    }
5761
5762    /// Matches any non-EOF token.
5763    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
5764        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5765            line: 0,
5766            column: 0,
5767            message: "missing current token".to_owned(),
5768        })?;
5769        if self.token_type_for_id(current) == TOKEN_EOF {
5770            return Err(AntlrError::MismatchedInput {
5771                expected: "wildcard".to_owned(),
5772                found: self.vocabulary().display_name(TOKEN_EOF),
5773            });
5774        }
5775        self.consume();
5776        Ok(self.terminal_tree(current))
5777    }
5778
5779    /// Generated parser synchronization hook. The current interpreter owns
5780    /// recovery; direct generated methods can call this as a no-op until the
5781    /// generated recovery strategy is expanded.
5782    #[allow(clippy::unnecessary_wraps)]
5783    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
5784        self.set_state(state);
5785        Ok(())
5786    }
5787
5788    /// Synchronizes a generated parser decision against the ATN lookahead set.
5789    ///
5790    /// ANTLR generated parsers call the error strategy before optional and loop
5791    /// decisions. When the current token cannot start any alternative, follow a
5792    /// nullable exit, or be deleted before a later synchronization token, the
5793    /// generated Rust method reports that decision-level mismatch instead of
5794    /// descending into a child rule that cannot start at the current token.
5795    pub fn sync_decision(
5796        &mut self,
5797        atn: &Atn,
5798        state_number: usize,
5799        current_context_empty: bool,
5800        loop_back: bool,
5801    ) -> Result<Vec<ParseTree>, AntlrError> {
5802        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
5803        self.generated_sync_expected = None;
5804        let Some(state) = atn.state(state_number) else {
5805            return Ok(Vec::new());
5806        };
5807        let Some(rule_index) = state.rule_index() else {
5808            return Ok(Vec::new());
5809        };
5810        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
5811            return Ok(Vec::new());
5812        };
5813        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
5814        let symbol = self.la(1);
5815        let mut has_expected_symbols = false;
5816        let mut nullable = false;
5817        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
5818        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
5819        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
5820        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
5821        // and worth deleting; an implicit-follow EOF means the loop should simply
5822        // exit and leave the token for the (absent) caller — matching ANTLR, which
5823        // exits the loop via prediction rather than consuming up to a synthetic EOF.
5824        let mut explicit_eof_expected = false;
5825        for transition in &entry.transitions {
5826            if transition.symbols.contains(symbol) {
5827                return Ok(Vec::new());
5828            }
5829            has_expected_symbols |= !transition.symbols.is_empty();
5830            nullable |= transition.nullable;
5831            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
5832        }
5833        // Happy path: a nullable decision exits when the symbol is in the
5834        // rule-stack follow set. Answer the membership question with an
5835        // early-exit walk; the full union below is only needed for the
5836        // mismatch/deletion diagnostics.
5837        if nullable && self.context_expected_contains(atn, symbol) {
5838            return Ok(Vec::new());
5839        }
5840        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
5841        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
5842            return Ok(Vec::new());
5843        }
5844        let mut expected = TokenBitSet::default();
5845        for transition in &entry.transitions {
5846            expected.extend_from(&transition.symbols);
5847        }
5848        if let Some(context_expected) = context_expected {
5849            expected.extend_from(&context_expected);
5850        }
5851        let can_delete_in_place =
5852            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
5853        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
5854        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
5855        // least one iteration) does `consumeUntil` the follow set — multi-token
5856        // deletion, one error per skipped token across iterations; a loop ENTRY
5857        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
5858        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
5859        // unexpected token only when LA(2) is expected, otherwise report a mismatch
5860        // and leave recovery to the rule.
5861        //
5862        // The generated loop always presents the loop-ENTRY state to this method on
5863        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
5864        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
5865        // an iteration has been taken, and true on a `+` loop's first sync since its
5866        // mandatory first element is iteration 1). Treating a loop entry as a
5867        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
5868        // `c`s, which ANTLR rejects with `mismatched input`).
5869        let loop_sync = loop_back;
5870        if symbol != TOKEN_EOF && can_delete_in_place {
5871            let mut cursor = self.input.index();
5872            let mut skipped = Vec::new();
5873            loop {
5874                let current = self.token_type_at(cursor);
5875                if current == TOKEN_EOF {
5876                    break;
5877                }
5878                skipped.push(cursor);
5879                let next = self.consume_index(cursor, current);
5880                if next == cursor {
5881                    break;
5882                }
5883                let next_symbol = self.token_type_at(next);
5884                // Stop (and delete the skipped tokens as error nodes) when the next
5885                // token is a real expected continuation. EOF counts only when it is
5886                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
5887                // genuinely extraneous and the generated EOF match consumes the real
5888                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
5889                // rule-follow) does NOT count — the loop must exit and leave the
5890                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
5891                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
5892                    explicit_eof_expected
5893                } else {
5894                    expected.contains(next_symbol)
5895                };
5896                if next_is_expected_stop {
5897                    let current_token = self.input.lt(1);
5898                    let expected_symbols = expected.to_btree_set();
5899                    let message = format!(
5900                        "extraneous input {} expecting {}",
5901                        current_token
5902                            .as_ref()
5903                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5904                        self.expected_symbols_display(&expected_symbols)
5905                    );
5906                    self.push_generated_parser_diagnostic(diagnostic_for_token(
5907                        current_token,
5908                        message,
5909                    ));
5910                    self.record_syntax_errors(1);
5911                    let mut children = Vec::with_capacity(skipped.len());
5912                    for index in skipped {
5913                        if let Some(token) = self.token_id_at(index) {
5914                            self.consume();
5915                            children.push(self.error_tree(token));
5916                        }
5917                    }
5918                    return Ok(children);
5919                }
5920                // A non-loop block entry deletes at most one token (single-token
5921                // deletion): if LA(2) is not expected, stop scanning so the mismatch
5922                // is reported at the first token instead of skipping ahead.
5923                if !loop_sync {
5924                    break;
5925                }
5926                cursor = next;
5927            }
5928        }
5929        if nullable {
5930            self.generated_sync_expected = Some(expected);
5931            return Ok(Vec::new());
5932        }
5933        let current = self.input.lt(1);
5934        let expected_symbols = expected.to_btree_set();
5935        Err(AntlrError::ParserError {
5936            line: current.as_ref().map(Token::line).unwrap_or_default(),
5937            column: current.as_ref().map(Token::column).unwrap_or_default(),
5938            message: format!(
5939                "mismatched input {} expecting {}",
5940                current
5941                    .as_ref()
5942                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5943                self.expected_symbols_display(&expected_symbols)
5944            ),
5945        })
5946    }
5947
5948    /// Returns a generated-parser prediction when one token of lookahead
5949    /// uniquely selects an alternative for `state_number`.
5950    ///
5951    /// This mirrors the interpreter's LL(1) commit point and lets generated
5952    /// recursive-descent methods avoid invoking the adaptive simulator for
5953    /// simple optional/block/loop decisions.
5954    pub fn ll1_decision_prediction(
5955        &mut self,
5956        atn: &Atn,
5957        state_number: usize,
5958    ) -> Option<ParserAtnPrediction> {
5959        let state = atn.state(state_number)?;
5960        if state.precedence_rule_decision() {
5961            return None;
5962        }
5963        let rule_stop = state
5964            .rule_index()
5965            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
5966        let symbol = self.la(1);
5967        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
5968        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
5969            alt: alt + 1,
5970            requires_full_context: false,
5971            has_semantic_context: false,
5972            diagnostic: None,
5973        })
5974    }
5975
5976    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
5977        let mut expected = BTreeSet::new();
5978        for index in (1..self.rule_context_stack.len()).rev() {
5979            let invoking_state = self.rule_context_stack[index].invoking_state;
5980            let Ok(state_number) = usize::try_from(invoking_state) else {
5981                continue;
5982            };
5983            let Some(Transition::Rule { follow_state, .. }) = atn
5984                .state(state_number)
5985                .and_then(|state| state.transitions().first())
5986                .map(ParserTransition::data)
5987            else {
5988                continue;
5989            };
5990            let return_state = follow_state;
5991            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
5992            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
5993                return expected;
5994            }
5995        }
5996        expected.insert(TOKEN_EOF);
5997        expected
5998    }
5999
6000    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6001        let mut expected = TokenBitSet::default();
6002        for index in (1..self.rule_context_stack.len()).rev() {
6003            let invoking_state = self.rule_context_stack[index].invoking_state;
6004            let Ok(state_number) = usize::try_from(invoking_state) else {
6005                continue;
6006            };
6007            let Some(Transition::Rule { follow_state, .. }) = atn
6008                .state(state_number)
6009                .and_then(|state| state.transitions().first())
6010                .map(ParserTransition::data)
6011            else {
6012                continue;
6013            };
6014            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
6015            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6016                return expected;
6017            }
6018        }
6019        expected.insert(TOKEN_EOF);
6020        expected
6021    }
6022
6023    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
6024    /// without materializing the union.
6025    ///
6026    /// This walks the rule-invocation stack directly, innermost frame first —
6027    /// the same frames, in the same order, with the same rule-stop gating as
6028    /// the same outer-context return-state chain used by adaptive prediction.
6029    /// The nullable
6030    /// exit in `sync_decision` asks only this membership question, and on
6031    /// valid input the innermost frame answers it, so the early exit replaces
6032    /// an O(stack-depth) set union per loop/optional exit with one probe.
6033    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
6034        for index in (1..self.rule_context_stack.len()).rev() {
6035            let invoking_state = self.rule_context_stack[index].invoking_state;
6036            let Ok(state_number) = usize::try_from(invoking_state) else {
6037                continue;
6038            };
6039            let Some(Transition::Rule { follow_state, .. }) = atn
6040                .state(state_number)
6041                .and_then(|state| state.transitions().first())
6042                .map(ParserTransition::data)
6043            else {
6044                continue;
6045            };
6046            if self
6047                .cached_state_expected_token_set(atn, follow_state)
6048                .contains(symbol)
6049            {
6050                return true;
6051            }
6052            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6053                return false;
6054            }
6055        }
6056        symbol == TOKEN_EOF
6057    }
6058
6059    /// Builds a generated no-viable-alternative parser error.
6060    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
6061        let error_index = self.input.index();
6062        self.no_viable_alternative_error_at(start_index, error_index)
6063    }
6064
6065    /// Builds a generated no-viable-alternative parser error at the simulator's
6066    /// failing lookahead index. `adaptive_predict` restores the input cursor
6067    /// before returning, so generated parsers have to pass the recorded index
6068    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
6069    pub fn no_viable_alternative_error_at(
6070        &self,
6071        start_index: usize,
6072        error_index: usize,
6073    ) -> AntlrError {
6074        let diagnostic = self.no_viable_alternative(start_index, error_index);
6075        AntlrError::ParserError {
6076            line: diagnostic.line,
6077            column: diagnostic.column,
6078            message: diagnostic.message,
6079        }
6080    }
6081
6082    /// Builds a generated failed-predicate parser error.
6083    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
6084        let current = self.input.lt(1);
6085        AntlrError::ParserError {
6086            line: current.as_ref().map(Token::line).unwrap_or_default(),
6087            column: current.as_ref().map(Token::column).unwrap_or_default(),
6088            message: format!("rule failed predicate: {}", message.into()),
6089        }
6090    }
6091
6092    /// Builds a generated parser error for a semantic predicate with ANTLR's
6093    /// `<fail='...'>` option.
6094    pub fn failed_predicate_option_error(
6095        &self,
6096        rule_index: usize,
6097        message: impl Into<String>,
6098    ) -> AntlrError {
6099        let current = self.input.lt(1);
6100        let rule_name = self
6101            .rule_names()
6102            .get(rule_index)
6103            .map_or_else(|| rule_index.to_string(), Clone::clone);
6104        AntlrError::ParserError {
6105            line: current.as_ref().map(Token::line).unwrap_or_default(),
6106            column: current.as_ref().map(Token::column).unwrap_or_default(),
6107            message: format!("rule {rule_name} {}", message.into()),
6108        }
6109    }
6110
6111    /// Builds a generated parser-action event at the current input position.
6112    pub fn parser_action_at_current(
6113        &mut self,
6114        source_state: usize,
6115        rule_index: usize,
6116        start_index: usize,
6117        consumed_eof: bool,
6118    ) -> ParserAction {
6119        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6120        ParserAction::new(source_state, rule_index, start_index, stop_index)
6121    }
6122
6123    /// Offers a committed parser action event to the user semantic hook.
6124    ///
6125    /// Generated parsers call this for action source states that were present
6126    /// in the ATN but not translated into a built-in Rust action template.
6127    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
6128        let rule_index = action.rule_index();
6129        let rule_name = self.rule_names().get(rule_index).cloned();
6130        let context = None;
6131        let input = &mut self.input;
6132        let semantic_hooks = &mut self.semantic_hooks;
6133        let member_values = &self.int_members;
6134        let mut ctx = ParserSemCtx {
6135            input,
6136            tree_storage: &self.tree,
6137            rule_index,
6138            coordinate_index: usize::MAX,
6139            rule_name,
6140            context,
6141            tree: Some(tree),
6142            local_int_arg: None,
6143            member_values,
6144            action: Some(action),
6145        };
6146        let handled = semantic_hooks.action(&mut ctx, action);
6147        // This action reached the hook because it had no translated arm. If no
6148        // hook handled it either (`SemanticHooks::action` returns `false`), the
6149        // committed action is silently dropped — record it so the parse entry
6150        // can fail loud under the fail-loud boundary, mirroring unknown
6151        // predicates. `assume-*` policies opt out of the fail-loud recording.
6152        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
6153            let coordinate = (rule_index, action.source_state());
6154            if !self.unhandled_action_hits.contains(&coordinate) {
6155                self.unhandled_action_hits.push(coordinate);
6156            }
6157        }
6158        handled
6159    }
6160
6161    /// Attempts to execute a whole generated rule by committing simulator
6162    /// decisions directly. Unsupported constructs or decisions that need
6163    /// full-context / predicate evaluation restore the input cursor and fall
6164    /// back to [`Self::parse_atn_rule`].
6165    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
6166        &mut self,
6167        atn: &'atn Atn,
6168        simulator: &mut ParserAtnSimulator<'atn>,
6169        rule_index: usize,
6170    ) -> Result<ParseTree, AntlrError> {
6171        let start_index = self.current_visible_index();
6172        self.clear_prediction_diagnostics();
6173        self.reset_per_parse_caches();
6174        self.reset_recognition_arena();
6175        let tree_checkpoint = self.tree.checkpoint();
6176        let mut decision_by_state = vec![None; atn.states().len()];
6177        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
6178            if let Some(slot) = decision_by_state.get_mut(state_number) {
6179                *slot = Some(decision);
6180            }
6181        }
6182
6183        let result = DirectAdaptiveParser {
6184            parser: self,
6185            atn,
6186            simulator,
6187            decision_by_state,
6188            steps: 0,
6189        }
6190        .parse_rule(rule_index, -1, 0);
6191
6192        match result {
6193            Ok(tree) => {
6194                report_token_source_errors(&self.input.drain_source_errors());
6195                self.release_tree_scratch_if_idle();
6196                Ok(tree)
6197            }
6198            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
6199                let _ = reason;
6200                self.tree.rollback(tree_checkpoint);
6201                self.input.seek(start_index);
6202                self.parse_atn_rule(atn, rule_index)
6203            }
6204        }
6205    }
6206
6207    /// Parses a generated rule by interpreting the parser ATN from the rule's
6208    /// start state to its stop state.
6209    ///
6210    /// The recognizer backtracks across alternatives and loop exits using token
6211    /// stream indices instead of committing to input consumption immediately.
6212    /// Once a viable ATN path is found, the parser commits the accepted token
6213    /// interval and returns a rule node whose children mirror every grammar
6214    /// rule invocation reached on that path, matching ANTLR's parse-tree
6215    /// shape.
6216    pub fn parse_atn_rule(
6217        &mut self,
6218        atn: &Atn,
6219        rule_index: usize,
6220    ) -> Result<ParseTree, AntlrError> {
6221        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
6222    }
6223
6224    /// Parses a generated rule by interpreting the parser ATN with an initial
6225    /// left-recursive precedence threshold.
6226    pub fn parse_atn_rule_with_precedence(
6227        &mut self,
6228        atn: &Atn,
6229        rule_index: usize,
6230        precedence: i32,
6231    ) -> Result<ParseTree, AntlrError> {
6232        self.parse_atn_rule_with_precedence_inner(atn, rule_index, precedence, None)
6233    }
6234
6235    fn parse_atn_rule_with_precedence_inner(
6236        &mut self,
6237        atn: &Atn,
6238        rule_index: usize,
6239        precedence: i32,
6240        predicate_context: Option<FastPredicateContext<'_>>,
6241    ) -> Result<ParseTree, AntlrError> {
6242        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6243            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6244        })?;
6245        let stop_state = atn
6246            .rule_to_stop_state()
6247            .get(rule_index)
6248            .filter(|state| *state != usize::MAX)
6249            .ok_or_else(|| {
6250                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6251            })?;
6252
6253        let start_index = self.current_visible_index();
6254        self.clear_prediction_diagnostics();
6255        self.reset_per_parse_caches();
6256        self.reset_recognition_arena();
6257        let caller_follow_state = self.pending_invoking_follow_state(atn);
6258        self.fast_recovery_enabled = false;
6259        self.fast_token_nodes_enabled = false;
6260        let top_request = FastRecognizeTopRequest {
6261            start_state,
6262            stop_state,
6263            start_index,
6264            precedence,
6265            caller_follow_state,
6266        };
6267        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
6268        self.fast_token_nodes_enabled = true;
6269        let needs_tree_retry = matches!(
6270            &first_pass,
6271            Ok((outcome, _))
6272                if self.build_parse_trees
6273                    && self
6274                        .recognition_arena
6275                        .sequence_has_left_recursive_boundary(outcome.nodes)
6276        );
6277        let needs_retry = match &first_pass {
6278            // The FIRST-set prefilter trims speculative rule calls that can't
6279            // match the current lookahead — useful for perf on grammars with
6280            // many epsilon-reachable rules, but the trim also bypasses
6281            // single-token insertion / deletion recovery that ANTLR's
6282            // reference parser runs at the child rule's first consuming
6283            // transition. Retry without the prefilter whenever the first pass
6284            // either produced no outcome at all or produced a recovered
6285            // outcome (diagnostics non-empty), since the second pass might
6286            // surface a child-level recovery with cleaner diagnostics or
6287            // closer parity to ANTLR's tree shape. Left-recursive tree
6288            // boundaries also need the token-node pass; otherwise the fold has
6289            // no concrete left operand to wrap into ANTLR's recursive context.
6290            Err(_) => true,
6291            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
6292        };
6293        let (outcome, _expected) = if needs_retry {
6294            self.fast_first_set_prefilter = false;
6295            self.fast_recovery_enabled = false;
6296            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6297            let clean_selected = if needs_tree_retry {
6298                match clean_retry {
6299                    ok @ Ok(_) => ok,
6300                    Err(_) => first_pass,
6301                }
6302            } else {
6303                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
6304            };
6305            let selected = if clean_selected.is_err()
6306                || matches!(&clean_selected, Ok((outcome, _)) if !outcome.diagnostics.is_empty())
6307            {
6308                self.fast_recovery_enabled = true;
6309                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6310                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
6311            } else {
6312                clean_selected
6313            };
6314            self.fast_first_set_prefilter = true;
6315            self.fast_recovery_enabled = true;
6316            selected.map_err(|expected| {
6317                if predicate_context.is_some()
6318                    && let Some(error) = self.unknown_semantic_error()
6319                {
6320                    report_token_source_errors(&self.input.drain_source_errors());
6321                    return error;
6322                }
6323                let error = self.recognition_error(rule_index, start_index, &expected);
6324                self.record_syntax_errors(1);
6325                report_token_source_errors(&self.input.drain_source_errors());
6326                error
6327            })?
6328        } else {
6329            first_pass.expect("first_pass is Ok in the no-retry branch")
6330        };
6331        if predicate_context.is_some()
6332            && let Some(error) = self.unknown_semantic_error()
6333        {
6334            report_token_source_errors(&self.input.drain_source_errors());
6335            return Err(error);
6336        }
6337        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6338        report_parser_diagnostics(&self.prediction_diagnostics);
6339        report_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6340        report_token_source_errors(&self.input.drain_source_errors());
6341        let mut context = ParserRuleContext::with_child_capacity(
6342            rule_index,
6343            self.state(),
6344            if self.build_parse_trees {
6345                self.recognition_arena.sequence_len(outcome.nodes)
6346            } else {
6347                0
6348            },
6349        );
6350        if let Some(token) = self.token_id_at(start_index) {
6351            self.set_context_start(&mut context, token);
6352        }
6353        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
6354        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
6355            self.set_context_stop(&mut context, token);
6356        }
6357        let live_root = if self.build_parse_trees {
6358            self.recognition_arena
6359                .fold_left_recursive_boundaries(outcome.nodes)
6360        } else {
6361            outcome.nodes
6362        };
6363        if self.build_parse_trees {
6364            if self
6365                .recognition_arena
6366                .sequence_has_explicit_token(live_root)
6367            {
6368                let mut cursor = live_root;
6369                while let Some(link) = self.recognition_arena.link(cursor) {
6370                    let child = self.arena_recognized_node_tree(link.head, false)?;
6371                    self.tree.add_child(&mut context, child);
6372                    cursor = link.tail;
6373                }
6374            } else {
6375                self.add_arena_implicit_token_children(
6376                    &mut context,
6377                    start_index,
6378                    stop_index,
6379                    live_root,
6380                )?;
6381            }
6382        }
6383        self.finish_recognition_arena(live_root, outcome.diagnostics);
6384        self.input.seek(outcome.index);
6385
6386        let tree = self.rule_node(context);
6387        self.release_tree_scratch_if_idle();
6388        Ok(tree)
6389    }
6390
6391    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
6392        let invoking_state = self.pending_invoking_states.last().copied()?;
6393        let state_number = usize::try_from(invoking_state).ok()?;
6394        match atn.state(state_number)?.transitions().first()?.data() {
6395            Transition::Rule { follow_state, .. } => Some(follow_state),
6396            _ => None,
6397        }
6398    }
6399
6400    #[cfg(test)]
6401    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
6402        caller_follow_token_info_for_stream(&mut self.input, index)
6403    }
6404
6405    /// Runs the fast recognizer once from the rule's start state and returns
6406    /// the best outcome or the per-attempt expected-token accumulator. The
6407    /// caller flips `fast_first_set_prefilter` between calls when a retry is
6408    /// needed, so the FIRST-set cache is left intact across both passes.
6409    fn fast_recognize_top(
6410        &mut self,
6411        atn: &Atn,
6412        request: FastRecognizeTopRequest,
6413        predicate_context: Option<FastPredicateContext<'_>>,
6414    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
6415        let FastRecognizeTopRequest {
6416            start_state,
6417            stop_state,
6418            start_index,
6419            precedence,
6420            caller_follow_state,
6421        } = request;
6422        // `input.size()` is intentionally only the currently buffered token
6423        // count here. Do not restore an up-front fill just to size this map:
6424        // the fixed floor avoids small-input churn, and large inputs grow the
6425        // cache after the deferred-fill threshold without forcing startup
6426        // tokenization. The 8x multiplier matches the empirical
6427        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
6428        // Kotlin ~12× memo entries per token), so the table avoids one
6429        // rehash on the typical hot path.
6430        let memo_capacity = self.input.size().saturating_mul(8).clamp(65_536, 524_288);
6431        let mut visiting = FxHashSet::with_capacity_and_hasher(256, FxBuildHasher::default());
6432        let mut memo = FxHashMap::with_capacity_and_hasher(memo_capacity, FxBuildHasher::default());
6433        let mut expected = ExpectedTokens::default();
6434        let empty_recovery = self.empty_recovery_symbols();
6435        let outcomes = self.recognize_state_fast(
6436            atn,
6437            FastRecognizeRequest {
6438                state_number: start_state,
6439                stop_state,
6440                index: start_index,
6441                rule_start_index: start_index,
6442                decision_start_index: None,
6443                precedence,
6444                depth: 0,
6445                recovery_symbols: empty_recovery,
6446                recovery_state: None,
6447            },
6448            FastRecognizeScratch {
6449                predicate_context,
6450                visiting: &mut visiting,
6451                memo: &mut memo,
6452                expected: &mut expected,
6453            },
6454        );
6455        #[cfg(feature = "perf-counters")]
6456        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
6457            perf_counters::dump();
6458            perf_counters::reset();
6459        }
6460        let caller_follow =
6461            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
6462        let selected = {
6463            let arena = &self.recognition_arena;
6464            let input = &mut self.input;
6465            select_best_fast_outcome(
6466                outcomes.into_iter(),
6467                self.prediction_mode,
6468                caller_follow.as_deref(),
6469                |index| caller_follow_token_info_for_stream(input, index),
6470                arena,
6471            )
6472        };
6473        match selected {
6474            Some(mut outcome) => {
6475                self.materialize_fast_outcome_nodes(&mut outcome);
6476                Ok((outcome, expected))
6477            }
6478            None => Err(expected),
6479        }
6480    }
6481
6482    /// Converts one speculative arena record into the flat public CST.
6483    fn arena_recognized_node_tree(
6484        &mut self,
6485        node_id: RecognizedNodeId,
6486        track_alt_numbers: bool,
6487    ) -> Result<ParseTree, AntlrError> {
6488        let node = self.recognition_arena.node(node_id);
6489        match node {
6490            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
6491            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
6492            ArenaRecognizedNode::MissingToken { extra } => {
6493                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
6494                    RecognitionExtra::MissingToken {
6495                        token_type,
6496                        at_index,
6497                        text,
6498                    } => (*token_type, *at_index as usize, text.clone()),
6499                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
6500                        unreachable!("missing-token node must reference missing-token extra")
6501                    }
6502                };
6503                let (line, column) = self
6504                    .token_at(at_index)
6505                    .map_or((0, 0), |token| (token.line(), token.column()));
6506                let token = self.insert_synthetic_token(token_type, text, line, column)?;
6507                Ok(self.error_tree(token))
6508            }
6509            ArenaRecognizedNode::Rule {
6510                rule_index,
6511                invoking_state,
6512                alt_number,
6513                start_index,
6514                stop_index,
6515                return_values,
6516                children,
6517            } => {
6518                let mut context = ParserRuleContext::with_child_capacity(
6519                    rule_index as usize,
6520                    invoking_state as isize,
6521                    self.recognition_arena.sequence_len(children),
6522                );
6523                if track_alt_numbers {
6524                    context.set_alt_number(alt_number as usize);
6525                }
6526                if let Some(extra) = return_values {
6527                    let RecognitionExtra::ReturnValues(values) =
6528                        self.recognition_arena.extra(extra)
6529                    else {
6530                        unreachable!("rule node must reference return-values extra");
6531                    };
6532                    for (name, value) in values {
6533                        context.set_int_return(name.clone(), *value);
6534                    }
6535                }
6536                if let Some(token) = self.token_id_at(start_index as usize) {
6537                    self.set_context_start(&mut context, token);
6538                }
6539                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6540                    self.set_context_stop(&mut context, token);
6541                }
6542                let mut cursor = self
6543                    .recognition_arena
6544                    .fold_left_recursive_boundaries(children);
6545                while let Some(link) = self.recognition_arena.link(cursor) {
6546                    let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
6547                    self.tree.add_child(&mut context, child);
6548                    cursor = link.tail;
6549                }
6550                Ok(self.rule_node(context))
6551            }
6552            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
6553                Err(AntlrError::Unsupported(format!(
6554                    "unfolded left-recursive boundary for rule {rule_index}"
6555                )))
6556            }
6557        }
6558    }
6559
6560    fn arena_recognized_node_tree_with_implicit_tokens(
6561        &mut self,
6562        node_id: RecognizedNodeId,
6563    ) -> Result<ParseTree, AntlrError> {
6564        let node = self.recognition_arena.node(node_id);
6565        match node {
6566            ArenaRecognizedNode::Rule {
6567                rule_index,
6568                invoking_state,
6569                start_index,
6570                stop_index,
6571                children,
6572                ..
6573            } => {
6574                let mut context = ParserRuleContext::with_child_capacity(
6575                    rule_index as usize,
6576                    invoking_state as isize,
6577                    self.recognition_arena.sequence_len(children),
6578                );
6579                if let Some(token) = self.token_id_at(start_index as usize) {
6580                    self.set_context_start(&mut context, token);
6581                }
6582                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6583                    self.set_context_stop(&mut context, token);
6584                }
6585                let children = self
6586                    .recognition_arena
6587                    .fold_left_recursive_boundaries(children);
6588                self.add_arena_implicit_token_children(
6589                    &mut context,
6590                    start_index as usize,
6591                    stop_index.map(|index| index as usize),
6592                    children,
6593                )?;
6594                Ok(self.rule_node(context))
6595            }
6596            _ => self.arena_recognized_node_tree(node_id, false),
6597        }
6598    }
6599
6600    fn add_arena_implicit_token_children(
6601        &mut self,
6602        context: &mut ParserRuleContext,
6603        start_index: usize,
6604        stop_index: Option<usize>,
6605        mut children: NodeSeqId,
6606    ) -> Result<(), AntlrError> {
6607        let mut cursor = Some(start_index);
6608        while let Some(link) = self.recognition_arena.link(children) {
6609            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
6610                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
6611                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6612                self.tree.add_child(context, child);
6613                if let Some(child_stop) = child_stop {
6614                    cursor = self.next_visible_after_token(child_stop);
6615                }
6616            } else {
6617                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6618                self.tree.add_child(context, child);
6619            }
6620            children = link.tail;
6621        }
6622        if let Some(stop) = stop_index {
6623            self.add_visible_terminals_through(context, cursor, stop)?;
6624        }
6625        Ok(())
6626    }
6627
6628    fn add_visible_terminals_before(
6629        &mut self,
6630        context: &mut ParserRuleContext,
6631        cursor: &mut Option<usize>,
6632        before: usize,
6633    ) -> Result<(), AntlrError> {
6634        let Some(stop) = before.checked_sub(1) else {
6635            return Ok(());
6636        };
6637        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
6638        *cursor = next;
6639        Ok(())
6640    }
6641
6642    fn add_visible_terminals_through(
6643        &mut self,
6644        context: &mut ParserRuleContext,
6645        mut cursor: Option<usize>,
6646        stop: usize,
6647    ) -> Result<Option<usize>, AntlrError> {
6648        while let Some(index) = cursor {
6649            if index > stop {
6650                return Ok(Some(index));
6651            }
6652            let token = self
6653                .input
6654                .get_id(index)
6655                .ok_or_else(|| AntlrError::ParserError {
6656                    line: 0,
6657                    column: 0,
6658                    message: format!("missing token at index {index}"),
6659                })?;
6660            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
6661            let child = self.terminal_tree(token);
6662            self.tree.add_child(context, child);
6663            if is_eof {
6664                return Ok(None);
6665            }
6666            cursor = self.next_visible_after_token(index);
6667        }
6668        Ok(None)
6669    }
6670
6671    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
6672        let next = self.input.next_visible_after(index);
6673        (next != index).then_some(next)
6674    }
6675
6676    /// Parses a generated rule and returns semantic actions reached on the
6677    /// selected ATN path.
6678    ///
6679    /// This slower path preserves action ordering and token intervals for
6680    /// generated code that replays target-specific action templates after the
6681    /// recognizer has chosen one viable parse path.
6682    pub fn parse_atn_rule_with_actions(
6683        &mut self,
6684        atn: &Atn,
6685        rule_index: usize,
6686    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6687        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
6688    }
6689
6690    /// Parses a generated rule and emits ATN actions plus selected rule-init
6691    /// actions reached on the chosen path.
6692    ///
6693    /// Generated parsers use this when a grammar contains rule-level `@init`
6694    /// templates that must run for nested rule invocations. The runtime keeps
6695    /// the action list path-sensitive, so init templates are replayed only for
6696    /// rules that were actually entered by the selected parse.
6697    pub fn parse_atn_rule_with_action_inits(
6698        &mut self,
6699        atn: &Atn,
6700        rule_index: usize,
6701        init_action_rules: &[usize],
6702    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6703        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
6704    }
6705
6706    /// Parses a generated rule with optional semantic-action replay features.
6707    ///
6708    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
6709    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
6710    /// unchanged for grammars that do not request that target template.
6711    pub fn parse_atn_rule_with_action_options(
6712        &mut self,
6713        atn: &Atn,
6714        rule_index: usize,
6715        init_action_rules: &[usize],
6716        track_alt_numbers: bool,
6717    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6718        self.parse_atn_rule_with_runtime_options(
6719            atn,
6720            rule_index,
6721            ParserRuntimeOptions {
6722                init_action_rules,
6723                track_alt_numbers,
6724                ..ParserRuntimeOptions::default()
6725            },
6726        )
6727    }
6728
6729    /// Parses a generated rule with action replay and parser predicate support.
6730    ///
6731    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
6732    /// target-template predicate semantics emitted by the generator. Missing
6733    /// entries are treated as true so unsupported predicate-free grammars keep
6734    /// the previous unconditional transition behavior.
6735    pub fn parse_atn_rule_with_runtime_options(
6736        &mut self,
6737        atn: &Atn,
6738        rule_index: usize,
6739        options: ParserRuntimeOptions<'_>,
6740    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6741        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
6742    }
6743
6744    /// Parses a generated rule with action replay, parser predicate support,
6745    /// and an initial left-recursive precedence threshold.
6746    pub fn parse_atn_rule_with_runtime_options_and_precedence(
6747        &mut self,
6748        atn: &Atn,
6749        rule_index: usize,
6750        precedence: i32,
6751        options: ParserRuntimeOptions<'_>,
6752    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6753        let ParserRuntimeOptions {
6754            init_action_rules,
6755            track_alt_numbers,
6756            predicates,
6757            semantics,
6758            rule_args,
6759            member_actions,
6760            return_actions,
6761            unknown_predicate_policy,
6762        } = options;
6763        if init_action_rules.is_empty()
6764            && !track_alt_numbers
6765            && predicates.is_empty()
6766            && semantics.is_none()
6767            && rule_args.is_empty()
6768            && member_actions.is_empty()
6769            && return_actions.is_empty()
6770            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
6771            && !atn_has_observable_action_transitions(atn)
6772            && (!self.semantic_hooks.observes_parser_predicates()
6773                || !atn_has_predicate_transitions(atn))
6774        {
6775            return self
6776                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
6777                .map(|tree| (tree, Vec::new()));
6778        }
6779        if can_use_fast_predicate_recognizer(atn, &options) {
6780            self.unknown_predicate_policy = unknown_predicate_policy;
6781            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
6782            let member_values = self.int_members.clone();
6783            let result = self
6784                .parse_atn_rule_with_precedence_inner(
6785                    atn,
6786                    rule_index,
6787                    precedence,
6788                    Some(FastPredicateContext {
6789                        predicates,
6790                        semantics,
6791                        member_values: &member_values,
6792                    }),
6793                )
6794                .map(|tree| (tree, Vec::new()));
6795            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
6796                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
6797            }
6798            return result;
6799        }
6800        self.unknown_predicate_policy = unknown_predicate_policy;
6801        // A generated parent may have already recorded unknown-predicate
6802        // coordinates before descending into this (interpreted) child. Clearing
6803        // unconditionally would drop them before the parent's public entry
6804        // surfaces them, so stash and restore around this call: recognition sees
6805        // only the hits it records itself (so the fail-loud check below reflects
6806        // this rule), and the parent's prior hits are merged back afterward.
6807        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
6808        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6809            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6810        })?;
6811        let stop_state = atn
6812            .rule_to_stop_state()
6813            .get(rule_index)
6814            .filter(|state| *state != usize::MAX)
6815            .ok_or_else(|| {
6816                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6817            })?;
6818
6819        let start_index = self.current_visible_index();
6820        self.clear_prediction_diagnostics();
6821        self.reset_per_parse_caches();
6822        self.reset_recognition_arena();
6823        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
6824        let invoking_state = self.pending_invoking_states.pop();
6825        let local_int_arg = invoking_state
6826            .and_then(|state| usize::try_from(state).ok())
6827            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
6828        let mut visiting = BTreeSet::new();
6829        let mut memo = BTreeMap::new();
6830        let mut expected = ExpectedTokens::default();
6831        let member_values = self.int_members.clone();
6832        let return_values = BTreeMap::new();
6833        let outcomes = self.recognize_state(
6834            atn,
6835            RecognizeRequest {
6836                state_number: start_state,
6837                stop_state,
6838                index: start_index,
6839                rule_start_index: start_index,
6840                decision_start_index: None,
6841                init_action_rules: &init_action_rules,
6842                predicates,
6843                semantics,
6844                rule_args,
6845                member_actions,
6846                return_actions,
6847                local_int_arg,
6848                member_values,
6849                return_values,
6850                rule_alt_number: 0,
6851                track_alt_numbers,
6852                consumed_eof: false,
6853                precedence,
6854                depth: 0,
6855                recovery_symbols: BTreeSet::new(),
6856                recovery_state: None,
6857            },
6858            &mut visiting,
6859            &mut memo,
6860            &mut expected,
6861        );
6862        if let Some(error) = self.unknown_semantic_error() {
6863            report_token_source_errors(&self.input.drain_source_errors());
6864            // Keep the recorded coordinates: when this interpreted rule is a
6865            // child of a generated parent, the parent's catch block recovers an
6866            // ordinary `AntlrError` into a partial subtree, so the fail-loud
6867            // coordinate must survive on the parser for the top-level entry's
6868            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
6869            // is handled by clearing at the top-level generated entry instead.
6870            return Err(error);
6871        }
6872        // Recognition recorded no unresolved coordinate of its own; merge the
6873        // parent's prior hits back so its public entry can still surface them.
6874        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
6875        let Some(outcome) = select_best_outcome(
6876            outcomes.into_iter(),
6877            self.prediction_mode,
6878            &self.recognition_arena,
6879        ) else {
6880            let error = self.recognition_error(rule_index, start_index, &expected);
6881            self.record_syntax_errors(1);
6882            report_token_source_errors(&self.input.drain_source_errors());
6883            return Err(error);
6884        };
6885
6886        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6887        report_parser_diagnostics(&self.prediction_diagnostics);
6888        report_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6889        report_token_source_errors(&self.input.drain_source_errors());
6890        let mut actions = outcome.actions;
6891        if init_action_rules.contains(&rule_index) {
6892            actions.insert(
6893                0,
6894                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
6895            );
6896        }
6897        let mut context =
6898            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
6899        if track_alt_numbers {
6900            context.set_alt_number(outcome.alt_number);
6901        }
6902        for (name, value) in outcome.return_values {
6903            context.set_int_return(name, value);
6904        }
6905        if let Some(token) = self.token_id_at(start_index) {
6906            self.set_context_start(&mut context, token);
6907        }
6908        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
6909            self.set_context_stop(&mut context, token);
6910        }
6911        let live_root = if self.build_parse_trees {
6912            self.recognition_arena
6913                .fold_left_recursive_boundaries(outcome.nodes)
6914        } else {
6915            outcome.nodes
6916        };
6917        if self.build_parse_trees {
6918            let mut nodes = live_root;
6919            while let Some(link) = self.recognition_arena.link(nodes) {
6920                let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
6921                self.tree.add_child(&mut context, child);
6922                nodes = link.tail;
6923            }
6924        }
6925        self.finish_recognition_arena(live_root, outcome.diagnostics);
6926        self.input.seek(outcome.index);
6927
6928        let tree = self.rule_node(context);
6929        self.release_tree_scratch_if_idle();
6930        Ok((tree, actions))
6931    }
6932
6933    /// Temporary parser entry used by generated parser methods while the parser
6934    /// ATN simulator is being implemented.
6935    ///
6936    /// This keeps generated parser crates buildable and gives us a stable method
6937    /// surface for every grammar rule. It intentionally accepts all remaining
6938    /// tokens into one rule context; it is not the final parser semantics.
6939    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
6940        let mut context = ParserRuleContext::new(rule_index, self.state());
6941        while self.la(1) != TOKEN_EOF {
6942            let token_type = self.la(1);
6943            let child = self.match_token(token_type)?;
6944            if self.build_parse_trees {
6945                self.tree.add_child(&mut context, child);
6946            }
6947        }
6948        if self.build_parse_trees {
6949            let child = self.match_eof()?;
6950            self.tree.add_child(&mut context, child);
6951        }
6952        let tree = self.rule_node(context);
6953        self.release_tree_scratch_if_idle();
6954        Ok(tree)
6955    }
6956
6957    /// Builds the parser error reported when no ATN path can reach the active
6958    /// rule stop state.
6959    fn recognition_error(
6960        &mut self,
6961        rule_index: usize,
6962        start_index: usize,
6963        expected: &ExpectedTokens,
6964    ) -> AntlrError {
6965        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
6966        self.input.seek(index);
6967        let current = self.input.lt(1);
6968        let line = current.as_ref().map(Token::line).unwrap_or_default();
6969        let column = current.as_ref().map(Token::column).unwrap_or_default();
6970        AntlrError::ParserError {
6971            line,
6972            column,
6973            message,
6974        }
6975    }
6976
6977    /// Builds the token index and ANTLR-compatible message for a failed rule.
6978    fn expected_error_message(
6979        &mut self,
6980        rule_index: usize,
6981        start_index: usize,
6982        expected: &ExpectedTokens,
6983    ) -> (usize, String) {
6984        let index = expected
6985            .index
6986            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
6987            .unwrap_or_else(|| self.input.index());
6988        self.input.seek(index);
6989        let current = self.input.lt(1);
6990        let message = if expected
6991            .no_viable
6992            .as_ref()
6993            .is_some_and(|no_viable| no_viable.error_index == index)
6994        {
6995            let start = expected
6996                .no_viable
6997                .as_ref()
6998                .map_or(start_index, |no_viable| no_viable.start_index);
6999            let text = display_input_text(&self.input.text(start, index));
7000            format!("no viable alternative at input '{text}'")
7001        } else if expected.symbols.is_empty() {
7002            if expected.index.is_some() {
7003                let found = current
7004                    .as_ref()
7005                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7006                if current
7007                    .as_ref()
7008                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7009                {
7010                    format!(
7011                        "missing {} at {found}",
7012                        self.expected_symbols_display(&expected.symbols)
7013                    )
7014                } else {
7015                    format!("mismatched input {found}")
7016                }
7017            } else {
7018                format!("no viable alternative while parsing rule {rule_index}")
7019            }
7020        } else {
7021            format!(
7022                "mismatched input {} expecting {}",
7023                current
7024                    .as_ref()
7025                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7026                self.expected_symbols_display(&expected.symbols)
7027            )
7028        };
7029        (index, message)
7030    }
7031
7032    /// Converts a failed child rule into a recovered outcome so the parent can
7033    /// continue after reporting the child diagnostic.
7034    fn child_rule_failure_recovery(
7035        &mut self,
7036        rule_index: usize,
7037        start_index: usize,
7038        sync_symbols: &BTreeSet<i32>,
7039        member_values: BTreeMap<usize, i64>,
7040        expected: &ExpectedTokens,
7041    ) -> Option<RecognizeOutcome> {
7042        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7043        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7044        let mut next_index = error_index;
7045        loop {
7046            let symbol = self.token_type_at(next_index);
7047            if sync_symbols.contains(&symbol) {
7048                if next_index == error_index {
7049                    return None;
7050                }
7051                break;
7052            }
7053            if symbol == TOKEN_EOF {
7054                break;
7055            }
7056            let after = self.consume_index(next_index, symbol);
7057            if after == next_index {
7058                break;
7059            }
7060            next_index = after;
7061        }
7062        let mut nodes = NodeSeqId::EMPTY;
7063        let error = self.arena_token_node(error_index, true);
7064        self.arena_prepend(&mut nodes, error);
7065        let diagnostics = self
7066            .recognition_arena
7067            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7068        Some(RecognizeOutcome {
7069            index: next_index,
7070            consumed_eof: false,
7071            alt_number: 0,
7072            member_values,
7073            return_values: BTreeMap::new(),
7074            diagnostics,
7075            decisions: Vec::new(),
7076            actions: Vec::new(),
7077            nodes,
7078        })
7079    }
7080
7081    /// Adapts the optional recovery result to the normal outcome list used by
7082    /// rule-call transitions.
7083    fn child_rule_failure_recovery_outcomes(
7084        &mut self,
7085        request: ChildRuleFailureRecovery<'_>,
7086    ) -> Vec<RecognizeOutcome> {
7087        let sync_symbols =
7088            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
7089        self.child_rule_failure_recovery(
7090            request.rule_index,
7091            request.start_index,
7092            &sync_symbols,
7093            request.member_values,
7094            request.expected,
7095        )
7096        .into_iter()
7097        .collect()
7098    }
7099
7100    /// Formats expected token types using ANTLR's single-token or set syntax.
7101    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
7102        expected_symbols_display(symbols, self.vocabulary())
7103    }
7104
7105    /// Returns the single-token deletion repair if the token after `index`
7106    /// satisfies the failed consuming transition.
7107    fn single_token_deletion(
7108        &mut self,
7109        transition: ParserTransition<'_>,
7110        index: usize,
7111        max_token_type: i32,
7112        expected_symbols: &BTreeSet<i32>,
7113    ) -> Option<(ParserDiagnostic, usize, i32)> {
7114        let current_symbol = self.token_type_at(index);
7115        if current_symbol == TOKEN_EOF {
7116            return None;
7117        }
7118        let next_index = self.consume_index(index, current_symbol);
7119        if next_index == index {
7120            return None;
7121        }
7122        let next_symbol = self.token_type_at(next_index);
7123        if !transition.matches(next_symbol, 1, max_token_type) {
7124            return None;
7125        }
7126        let transition_expected = transition_expected_symbols(transition, max_token_type);
7127        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7128            &transition_expected
7129        } else {
7130            expected_symbols
7131        });
7132        let current = self.token_at(index);
7133        let message = format!(
7134            "extraneous input {} expecting {expected_display}",
7135            current
7136                .as_ref()
7137                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7138        );
7139        Some((
7140            diagnostic_for_token(current, message),
7141            next_index,
7142            next_symbol,
7143        ))
7144    }
7145
7146    /// Returns the repair used when deleting the current token lets a recovery
7147    /// state continue with the following token.
7148    fn current_token_deletion(
7149        &mut self,
7150        index: usize,
7151        expected_symbols: &BTreeSet<i32>,
7152    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
7153        if expected_symbols.is_empty() {
7154            return None;
7155        }
7156        let current_symbol = self.token_type_at(index);
7157        if current_symbol == TOKEN_EOF {
7158            return None;
7159        }
7160        let current = self.token_at(index);
7161        let message = format!(
7162            "extraneous input {} expecting {}",
7163            current
7164                .as_ref()
7165                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7166            self.expected_symbols_display(expected_symbols)
7167        );
7168        let diagnostic = diagnostic_for_token(current, message);
7169        let mut skipped = Vec::new();
7170        let mut cursor = index;
7171        loop {
7172            let symbol = self.token_type_at(cursor);
7173            if symbol == TOKEN_EOF {
7174                return None;
7175            }
7176            skipped.push(cursor);
7177            let next_index = self.consume_index(cursor, symbol);
7178            if next_index == cursor {
7179                return None;
7180            }
7181            let next_symbol = self.token_type_at(next_index);
7182            if expected_symbols.contains(&next_symbol) {
7183                return Some((diagnostic, next_index, skipped));
7184            }
7185            cursor = next_index;
7186        }
7187    }
7188
7189    /// Returns the single-token insertion repair for a failed consuming
7190    /// transition. The caller validates the repair by continuing from the
7191    /// transition target at the same input index.
7192    fn single_token_insertion(
7193        &mut self,
7194        transition: ParserTransition<'_>,
7195        index: usize,
7196        max_token_type: i32,
7197        expected_symbols: &BTreeSet<i32>,
7198        follow_symbols: &BTreeSet<i32>,
7199    ) -> Option<(ParserDiagnostic, i32, String)> {
7200        let current_symbol = self.token_type_at(index);
7201        if !follow_symbols.contains(&current_symbol) {
7202            return None;
7203        }
7204        let transition_expected = transition_expected_symbols(transition, max_token_type);
7205        let token_type = transition_expected.iter().next().copied()?;
7206        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7207            &transition_expected
7208        } else {
7209            expected_symbols
7210        });
7211        let mut token_symbols = BTreeSet::new();
7212        token_symbols.insert(token_type);
7213        let missing_token_display = self.expected_symbols_display(&token_symbols);
7214        let current = self.token_at(index);
7215        let message = format!(
7216            "missing {expected_display} at {}",
7217            current
7218                .as_ref()
7219                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7220        );
7221        let text = format!("<missing {missing_token_display}>");
7222        Some((
7223            diagnostic_for_token(current.as_ref(), message),
7224            token_type,
7225            text,
7226        ))
7227    }
7228
7229    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
7230    /// skip the unexpected current token when the following token satisfies the
7231    /// transition that failed.
7232    fn fast_single_token_deletion_recovery(
7233        &mut self,
7234        recovery: FastRecoveryRequest<'_, '_>,
7235        predicate_context: Option<FastPredicateContext<'_>>,
7236    ) -> Vec<FastRecognizeOutcome> {
7237        let FastRecoveryRequest {
7238            atn,
7239            transition,
7240            expected_symbols,
7241            target,
7242            request,
7243            visiting,
7244            memo,
7245            expected,
7246        } = recovery;
7247        let FastRecognizeRequest {
7248            stop_state,
7249            index,
7250            rule_start_index,
7251            decision_start_index,
7252            precedence,
7253            depth,
7254            ..
7255        } = request;
7256        let Some((diagnostic, next_index, next_symbol)) =
7257            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7258        else {
7259            return Vec::new();
7260        };
7261        let after_next = self.consume_index(next_index, next_symbol);
7262        let empty_recovery = self.empty_recovery_symbols();
7263        self.recognize_state_fast(
7264            atn,
7265            FastRecognizeRequest {
7266                state_number: target,
7267                stop_state,
7268                index: after_next,
7269                rule_start_index,
7270                decision_start_index,
7271                precedence,
7272                depth: depth + 1,
7273                recovery_symbols: empty_recovery,
7274                recovery_state: None,
7275            },
7276            FastRecognizeScratch {
7277                predicate_context,
7278                visiting,
7279                memo,
7280                expected,
7281            },
7282        )
7283        .into_iter()
7284        .map(|mut outcome| {
7285            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7286            outcome.diagnostics = self
7287                .recognition_arena
7288                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7289            if self.fast_token_nodes_enabled {
7290                let token = self.arena_token_node(next_index, false);
7291                self.defer_fast_outcome_node(&mut outcome, token);
7292                let error = self.arena_token_node(index, true);
7293                self.defer_fast_outcome_node(&mut outcome, error);
7294            }
7295            outcome
7296        })
7297        .collect()
7298    }
7299
7300    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
7301    /// pretend the expected transition token was present and continue without
7302    /// consuming the current token.
7303    fn fast_single_token_insertion_recovery(
7304        &mut self,
7305        recovery: FastRecoveryRequest<'_, '_>,
7306        predicate_context: Option<FastPredicateContext<'_>>,
7307    ) -> Vec<FastRecognizeOutcome> {
7308        let FastRecoveryRequest {
7309            atn,
7310            transition,
7311            expected_symbols,
7312            target,
7313            request,
7314            visiting,
7315            memo,
7316            expected,
7317        } = recovery;
7318        let FastRecognizeRequest {
7319            stop_state,
7320            index,
7321            rule_start_index,
7322            decision_start_index,
7323            precedence,
7324            depth,
7325            ..
7326        } = request;
7327        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
7328        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7329            transition,
7330            index,
7331            atn.max_token_type(),
7332            &expected_symbols,
7333            &follow_symbols,
7334        ) else {
7335            return Vec::new();
7336        };
7337        let empty_recovery = self.empty_recovery_symbols();
7338        self.recognize_state_fast(
7339            atn,
7340            FastRecognizeRequest {
7341                state_number: target,
7342                stop_state,
7343                index,
7344                rule_start_index,
7345                decision_start_index,
7346                precedence,
7347                depth: depth + 1,
7348                recovery_symbols: empty_recovery,
7349                recovery_state: None,
7350            },
7351            FastRecognizeScratch {
7352                predicate_context,
7353                visiting,
7354                memo,
7355                expected,
7356            },
7357        )
7358        .into_iter()
7359        .map(|mut outcome| {
7360            outcome.diagnostics = self
7361                .recognition_arena
7362                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7363            let missing = self.arena_missing_token_node(token_type, index, text.clone());
7364            self.defer_fast_outcome_node(&mut outcome, missing);
7365            outcome
7366        })
7367        .collect()
7368    }
7369
7370    /// Retries the current fast-recognition state after deleting one
7371    /// unexpected token that precedes a valid loop or block continuation.
7372    fn fast_current_token_deletion_recovery(
7373        &mut self,
7374        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
7375        predicate_context: Option<FastPredicateContext<'_>>,
7376    ) -> Vec<FastRecognizeOutcome> {
7377        let FastCurrentTokenDeletionRequest {
7378            atn,
7379            expected_symbols,
7380            mut request,
7381            visiting,
7382            memo,
7383            expected,
7384        } = recovery;
7385        if request.index == request.rule_start_index {
7386            return Vec::new();
7387        }
7388        let Some((diagnostic, next_index, skipped)) =
7389            self.current_token_deletion(request.index, &expected_symbols)
7390        else {
7391            return Vec::new();
7392        };
7393        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7394        request.index = next_index;
7395        request.depth += 1;
7396        request.recovery_state = None;
7397        self.recognize_state_fast(
7398            atn,
7399            request,
7400            FastRecognizeScratch {
7401                predicate_context,
7402                visiting,
7403                memo,
7404                expected,
7405            },
7406        )
7407        .into_iter()
7408        .map(|mut outcome| {
7409            outcome.diagnostics = self
7410                .recognition_arena
7411                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7412            for index in skipped.iter().rev() {
7413                let error = self.arena_token_node(*index, true);
7414                self.defer_fast_outcome_node(&mut outcome, error);
7415            }
7416            outcome
7417        })
7418        .collect()
7419    }
7420
7421    /// Converts a failed child rule into a recovered fast-recognizer outcome so
7422    /// the parent can keep its child rule context and continue at a sync token.
7423    fn fast_child_rule_failure_recovery(
7424        &mut self,
7425        rule_index: usize,
7426        start_index: usize,
7427        sync_symbols: &BTreeSet<i32>,
7428        expected: &ExpectedTokens,
7429    ) -> Option<FastRecognizeOutcome> {
7430        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7431        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7432        let mut next_index = error_index;
7433        loop {
7434            let symbol = self.token_type_at(next_index);
7435            if sync_symbols.contains(&symbol) {
7436                if next_index == error_index {
7437                    return None;
7438                }
7439                break;
7440            }
7441            if symbol == TOKEN_EOF {
7442                break;
7443            }
7444            let after = self.consume_index(next_index, symbol);
7445            if after == next_index {
7446                break;
7447            }
7448            next_index = after;
7449        }
7450        let diagnostics = self
7451            .recognition_arena
7452            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7453        let mut nodes = NodeSeqId::EMPTY;
7454        if self.fast_token_nodes_enabled {
7455            let error = self.arena_token_node(error_index, true);
7456            self.arena_prepend(&mut nodes, error);
7457        }
7458        Some(FastRecognizeOutcome {
7459            index: next_index,
7460            consumed_eof: false,
7461            diagnostics,
7462            deferred_nodes: FastDeferredNodeId::EMPTY,
7463            nodes,
7464        })
7465    }
7466
7467    /// Adapts the optional child-rule recovery result to the fast-recognizer
7468    /// outcome list used by rule-call transitions.
7469    fn fast_child_rule_failure_recovery_outcomes(
7470        &mut self,
7471        request: FastChildRuleFailureRecoveryRequest<'_>,
7472    ) -> Vec<FastRecognizeOutcome> {
7473        let FastChildRuleFailureRecoveryRequest {
7474            atn,
7475            rule_index,
7476            start_index,
7477            follow_state,
7478            stop_state,
7479            expected,
7480        } = request;
7481        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
7482        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
7483            .into_iter()
7484            .collect()
7485    }
7486
7487    fn defer_fast_outcome_node(
7488        &mut self,
7489        outcome: &mut FastRecognizeOutcome,
7490        node: RecognizedNodeId,
7491    ) {
7492        if outcome.deferred_nodes.is_empty() {
7493            self.arena_prepend(&mut outcome.nodes, node);
7494            return;
7495        }
7496        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
7497        let fragment = self.recognition_arena.deferred_fragment(fragment);
7498        outcome.deferred_nodes = self
7499            .recognition_arena
7500            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
7501    }
7502
7503    fn materialize_fast_deferred_nodes(
7504        &mut self,
7505        root: FastDeferredNodeId,
7506        initial_suffix: NodeSeqId,
7507    ) -> NodeSeqId {
7508        if root.is_empty() {
7509            return initial_suffix;
7510        }
7511
7512        enum Frame {
7513            Visit(FastDeferredNodeId),
7514            ContinuePrefix(FastDeferredNodeId),
7515            FinishRule {
7516                rule: FastDeferredRule,
7517                parent_suffix: NodeSeqId,
7518            },
7519        }
7520
7521        let mut result = initial_suffix;
7522        let mut pending = Vec::with_capacity(16);
7523        pending.push(Frame::Visit(root));
7524        let mut fragment_nodes = Vec::new();
7525        while let Some(frame) = pending.pop() {
7526            match frame {
7527                Frame::Visit(deferred) => {
7528                    if deferred.is_empty() {
7529                        continue;
7530                    }
7531
7532                    match self.recognition_arena.deferred_node(deferred) {
7533                        FastDeferredNode::Fragment(sequence) => {
7534                            fragment_nodes.clear();
7535                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
7536                            while let Some(node) = fragment_nodes.pop() {
7537                                self.arena_prepend(&mut result, node);
7538                            }
7539                        }
7540                        FastDeferredNode::Rule(rule) => {
7541                            let rule = self.recognition_arena.deferred_rule(rule);
7542                            let parent_suffix = result;
7543                            result = rule.children;
7544                            pending.push(Frame::FinishRule {
7545                                rule,
7546                                parent_suffix,
7547                            });
7548                            pending.push(Frame::Visit(rule.deferred_children));
7549                        }
7550                        FastDeferredNode::Concat {
7551                            prefix,
7552                            suffix: deferred_suffix,
7553                        } => {
7554                            pending.push(Frame::ContinuePrefix(prefix));
7555                            pending.push(Frame::Visit(deferred_suffix));
7556                        }
7557                    }
7558                }
7559                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
7560                Frame::FinishRule {
7561                    rule,
7562                    parent_suffix,
7563                } => {
7564                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
7565                        rule_index: rule.rule_index,
7566                        invoking_state: rule.invoking_state,
7567                        alt_number: 0,
7568                        start_index: rule.start_index,
7569                        stop_index: rule.stop_index,
7570                        return_values: None,
7571                        children: result,
7572                    });
7573                    result = parent_suffix;
7574                    self.arena_prepend(&mut result, node);
7575                }
7576            }
7577        }
7578        result
7579    }
7580
7581    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) {
7582        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
7583        outcome.nodes = self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
7584    }
7585
7586    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
7587    /// heap work instead of the native call stack.
7588    fn recognize_repetition_fast(
7589        &mut self,
7590        atn: &Atn,
7591        request: &FastRecognizeRequest,
7592        shape: FastRepetitionShape,
7593        scratch: FastRecognizeScratch<'_, '_>,
7594    ) -> Vec<FastRecognizeOutcome> {
7595        let FastRecognizeScratch {
7596            predicate_context,
7597            visiting,
7598            memo,
7599            expected,
7600        } = scratch;
7601        let lookahead = if self.fast_first_set_prefilter {
7602            atn.state(request.state_number).and_then(|state| {
7603                state
7604                    .rule_index()
7605                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
7606                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
7607            })
7608        } else {
7609            None
7610        };
7611        let mut work = Vec::with_capacity(2);
7612        push_fast_repetition_work(
7613            &mut work,
7614            shape,
7615            FastRepetitionPath {
7616                index: request.index,
7617                deferred_nodes: FastDeferredNodeId::EMPTY,
7618                diagnostics: DiagnosticSeqId::EMPTY,
7619                consumed_eof: false,
7620            },
7621            lookahead.as_deref(),
7622            self.token_type_at(request.index),
7623        );
7624        let mut coordinates = FastRepetitionCoordinates::new(request.index);
7625        let mut outcomes = Vec::new();
7626        while let Some(item) = work.pop() {
7627            match item {
7628                FastRepetitionWork::Enter(path) => {
7629                    if !coordinates.insert_entered(path) {
7630                        continue;
7631                    }
7632                    let body_outcomes = self.recognize_state_fast(
7633                        atn,
7634                        FastRecognizeRequest {
7635                            state_number: shape.enter_target,
7636                            stop_state: shape.body_stop_state,
7637                            index: path.index,
7638                            rule_start_index: request.rule_start_index,
7639                            decision_start_index: request.decision_start_index,
7640                            precedence: request.precedence,
7641                            depth: request.depth.saturating_add(1),
7642                            recovery_symbols: Rc::clone(&request.recovery_symbols),
7643                            recovery_state: request.recovery_state,
7644                        },
7645                        FastRecognizeScratch {
7646                            predicate_context,
7647                            visiting: &mut *visiting,
7648                            memo: &mut *memo,
7649                            expected: &mut *expected,
7650                        },
7651                    );
7652                    for body in body_outcomes.into_iter().rev() {
7653                        // ANTLR rejects nullable repetition bodies. Keep the
7654                        // interpreter bounded for malformed or recovered ATNs
7655                        // by mirroring the existing same-coordinate cycle cut.
7656                        if body.index <= path.index {
7657                            continue;
7658                        }
7659                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
7660                        let body_nodes = self
7661                            .recognition_arena
7662                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
7663                        let deferred_nodes = self
7664                            .recognition_arena
7665                            .concat_deferred_nodes(path.deferred_nodes, body_nodes);
7666                        let next_path = FastRepetitionPath {
7667                            index: body.index,
7668                            deferred_nodes,
7669                            diagnostics: self
7670                                .recognition_arena
7671                                .concat_diagnostics(path.diagnostics, body.diagnostics),
7672                            consumed_eof: path.consumed_eof || body.consumed_eof,
7673                        };
7674                        let symbol = self.token_type_at(next_path.index);
7675                        push_fast_repetition_work(
7676                            &mut work,
7677                            shape,
7678                            next_path,
7679                            lookahead.as_deref(),
7680                            symbol,
7681                        );
7682                    }
7683                }
7684                FastRepetitionWork::Exit(path) => {
7685                    if !coordinates.insert_exited(path) {
7686                        continue;
7687                    }
7688                    let suffixes = self.recognize_state_fast(
7689                        atn,
7690                        FastRecognizeRequest {
7691                            state_number: shape.exit_target,
7692                            stop_state: request.stop_state,
7693                            index: path.index,
7694                            rule_start_index: request.rule_start_index,
7695                            decision_start_index: request.decision_start_index,
7696                            precedence: request.precedence,
7697                            depth: request.depth.saturating_add(1),
7698                            recovery_symbols: Rc::clone(&request.recovery_symbols),
7699                            recovery_state: request.recovery_state,
7700                        },
7701                        FastRecognizeScratch {
7702                            predicate_context,
7703                            visiting: &mut *visiting,
7704                            memo: &mut *memo,
7705                            expected: &mut *expected,
7706                        },
7707                    );
7708                    for mut outcome in suffixes {
7709                        outcome.deferred_nodes = self
7710                            .recognition_arena
7711                            .concat_deferred_nodes(path.deferred_nodes, outcome.deferred_nodes);
7712                        outcome.diagnostics = self
7713                            .recognition_arena
7714                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
7715                        outcome.consumed_eof |= path.consumed_eof;
7716                        outcomes.push(outcome);
7717                    }
7718                }
7719            }
7720        }
7721        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
7722        outcomes
7723    }
7724
7725    /// Attempts to reach `stop_state` from `state_number` without committing
7726    /// token consumption to the parser's public stream position.
7727    #[allow(clippy::too_many_lines)]
7728    fn recognize_state_fast(
7729        &mut self,
7730        atn: &Atn,
7731        request: FastRecognizeRequest,
7732        scratch: FastRecognizeScratch<'_, '_>,
7733    ) -> Vec<FastRecognizeOutcome> {
7734        #[cfg(feature = "perf-counters")]
7735        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
7736        let FastRecognizeScratch {
7737            predicate_context,
7738            visiting,
7739            memo,
7740            expected,
7741        } = scratch;
7742        let FastRecognizeRequest {
7743            mut state_number,
7744            stop_state,
7745            mut index,
7746            rule_start_index,
7747            decision_start_index,
7748            precedence,
7749            mut depth,
7750            recovery_symbols,
7751            recovery_state,
7752        } = request;
7753        let max_token_type = atn.max_token_type();
7754        // Walk straight-line epsilon chains in a loop instead of recursing
7755        // into `recognize_state_fast` for each intermediate state. ATN
7756        // serialization places long sequences of `BasicBlock` epsilon
7757        // transitions between decisions: turning that chain into a loop
7758        // collapses many recursive calls (and their memo lookups, vec
7759        // allocations, and visit-set churn) into a single function frame.
7760        // The loop exits as soon as we hit the original state's logic
7761        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
7762        // precedence) so existing fanout, recovery, and memoization still
7763        // apply unchanged.
7764        //
7765        // The inline case also handles single-atom-match states on the
7766        // happy-pass path: when the lone consuming transition matches the
7767        // current lookahead, advance the index and continue without paying
7768        // for a full `recognize_state_fast` recursion. We track tokens we
7769        // consumed inline in `inline_consumed_tokens` so they can be
7770        // prepended onto the eventual outcome list once we hit a state
7771        // whose handling falls outside this fast loop.
7772        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
7773        let mut inline_consumed_eof = false;
7774        loop {
7775            if depth > RECOGNITION_DEPTH_LIMIT {
7776                return Vec::new();
7777            }
7778            if state_number == stop_state {
7779                let mut nodes = NodeSeqId::EMPTY;
7780                if self.fast_token_nodes_enabled {
7781                    for token_index in inline_consumed_tokens.iter().rev() {
7782                        let token = self.arena_token_node(*token_index, false);
7783                        self.arena_prepend(&mut nodes, token);
7784                    }
7785                }
7786                return vec![FastRecognizeOutcome {
7787                    index,
7788                    consumed_eof: inline_consumed_eof,
7789                    diagnostics: DiagnosticSeqId::EMPTY,
7790                    deferred_nodes: FastDeferredNodeId::EMPTY,
7791                    nodes,
7792                }];
7793            }
7794            let Some(state) = atn.state(state_number) else {
7795                return Vec::new();
7796            };
7797            let transitions = state.transitions();
7798            if transitions.len() == 1 && !state.precedence_rule_decision() {
7799                let transition = transitions
7800                    .first()
7801                    .expect("single transition checked above");
7802                let transition_kind = transition.kind();
7803                let target = transition.target();
7804                match transition_kind {
7805                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
7806                        if left_recursive_boundary(atn, state, target).is_none() =>
7807                    {
7808                        #[cfg(feature = "perf-counters")]
7809                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
7810                        state_number = target;
7811                        depth += 1;
7812                        continue;
7813                    }
7814                    ParserTransitionKind::Predicate
7815                        if left_recursive_boundary(atn, state, target).is_none() =>
7816                    {
7817                        #[cfg(feature = "perf-counters")]
7818                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
7819                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
7820                        {
7821                            record_predicate_no_viable(expected, decision_start_index, index);
7822                            return Vec::new();
7823                        }
7824                        state_number = target;
7825                        depth += 1;
7826                        continue;
7827                    }
7828                    ParserTransitionKind::Precedence
7829                        if packed_i32(transition.arg0()) >= precedence
7830                            && left_recursive_boundary(atn, state, target).is_none() =>
7831                    {
7832                        #[cfg(feature = "perf-counters")]
7833                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
7834                        state_number = target;
7835                        depth += 1;
7836                        continue;
7837                    }
7838                    // Single-atom / range / set / wildcard / not-set states
7839                    // are common (~17K of ~125K calls on C#) and almost
7840                    // always succeed in pass 1: no fanout, no recovery, no
7841                    // diagnostics. Inline the token match and continue
7842                    // walking instead of recursing — the recursive path
7843                    // would just allocate a Vec, build one outcome, prepend
7844                    // a Token node, and return. Skip pass 2 (recovery
7845                    // enabled): there the failure branch matters and the
7846                    // existing recursive code records expected symbols.
7847                    ParserTransitionKind::Atom
7848                    | ParserTransitionKind::Range
7849                    | ParserTransitionKind::Set
7850                    | ParserTransitionKind::NotSet
7851                    | ParserTransitionKind::Wildcard
7852                        if !self.fast_recovery_enabled =>
7853                    {
7854                        let symbol = self.token_type_at(index);
7855                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
7856                            #[cfg(feature = "perf-counters")]
7857                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
7858                            if self.fast_token_nodes_enabled {
7859                                inline_consumed_tokens.push(index);
7860                            }
7861                            inline_consumed_eof |= symbol == TOKEN_EOF;
7862                            index = self.consume_index(index, symbol);
7863                            state_number = target;
7864                            depth += 1;
7865                            continue;
7866                        }
7867                        // Fall through to break and let the regular
7868                        // body handle the no-match case (returns empty).
7869                    }
7870                    _ => {}
7871                }
7872            }
7873            break;
7874        }
7875        // If we collected token nodes inline but bail to the recursive
7876        // body (decision state, rule call, etc.), the outcomes returned
7877        // below will need those token nodes prepended.
7878        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
7879        let Some(state) = atn.state(state_number) else {
7880            return Vec::new();
7881        };
7882        let transitions = state.transitions();
7883        let transition_count = transitions.len();
7884        if !self.fast_recovery_enabled
7885            && let Some(shape) = fast_repetition_shape(atn, state)
7886        {
7887            let mut outcomes = self.recognize_repetition_fast(
7888                atn,
7889                &FastRecognizeRequest {
7890                    state_number,
7891                    stop_state,
7892                    index,
7893                    rule_start_index,
7894                    decision_start_index,
7895                    precedence,
7896                    depth,
7897                    recovery_symbols: Rc::clone(&recovery_symbols),
7898                    recovery_state,
7899                },
7900                shape,
7901                FastRecognizeScratch {
7902                    predicate_context,
7903                    visiting: &mut *visiting,
7904                    memo: &mut *memo,
7905                    expected: &mut *expected,
7906                },
7907            );
7908            if inline_pending {
7909                for outcome in &mut outcomes {
7910                    outcome.consumed_eof |= inline_consumed_eof;
7911                    if self.fast_token_nodes_enabled {
7912                        for token_index in inline_consumed_tokens.iter().rev() {
7913                            let token = self.arena_token_node(*token_index, false);
7914                            self.defer_fast_outcome_node(outcome, token);
7915                        }
7916                    }
7917                }
7918            }
7919            return outcomes;
7920        }
7921        let memo_lookup_enabled = self.fast_recovery_enabled || transition_count > 1;
7922        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
7923        // fields and the rule/decision boundary indices are pure plumbing —
7924        // they only affect the recovery branch and the no-viable diagnostic
7925        // recording, neither of which fires when recovery is off. Zeroing
7926        // them in the memo key collapses calls that visit the same
7927        // `(state, index)` from different rule-call sites onto one cache
7928        // entry, which is the dominant cost on large grammars (e.g. C#) where
7929        // many rules eventually delegate into the same `expression` /
7930        // `primary_expression` / `type` branches.
7931        let key = if self.fast_recovery_enabled {
7932            FastRecognizeKey {
7933                state_number,
7934                stop_state,
7935                index,
7936                rule_start_index,
7937                decision_start_index,
7938                precedence,
7939                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
7940                recovery_state,
7941            }
7942        } else {
7943            FastRecognizeKey {
7944                state_number,
7945                stop_state,
7946                index,
7947                rule_start_index: 0,
7948                decision_start_index: None,
7949                precedence,
7950                recovery_symbols_id: 0,
7951                recovery_state: None,
7952            }
7953        };
7954        if memo_lookup_enabled {
7955            if let Some(outcomes) = memo.get(&key) {
7956                #[cfg(feature = "perf-counters")]
7957                {
7958                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
7959                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
7960                }
7961                // Materialize a fresh `Vec` from the cached slice; the caller
7962                // mutates per-outcome state (eof flags, prepended nodes) so we
7963                // can't hand them the shared backing.
7964                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
7965                    let inline_eof = inline_consumed_eof;
7966                    let inline_tokens = &inline_consumed_tokens;
7967                    return outcomes
7968                        .iter()
7969                        .copied()
7970                        .map(|mut outcome| {
7971                            if inline_eof {
7972                                outcome.consumed_eof = true;
7973                            }
7974                            if self.fast_token_nodes_enabled {
7975                                for token_index in inline_tokens.iter().rev() {
7976                                    let token = self.arena_token_node(*token_index, false);
7977                                    self.defer_fast_outcome_node(&mut outcome, token);
7978                                }
7979                            }
7980                            outcome
7981                        })
7982                        .collect();
7983                }
7984                return outcomes.to_vec();
7985            }
7986            #[cfg(feature = "perf-counters")]
7987            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
7988        }
7989
7990        // Cycle detection: clean recognition keeps the narrow static cycle
7991        // guard used on hot paths. Recovery needs the broader epsilon-state
7992        // guard because an otherwise non-nullable loop body can recover as an
7993        // empty child at EOF and re-enter the loop at the same token.
7994        let needs_cycle_guard = if self.fast_recovery_enabled {
7995            transitions.iter().any(ParserTransition::is_epsilon)
7996        } else {
7997            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
7998        };
7999        #[cfg(feature = "perf-counters")]
8000        if needs_cycle_guard {
8001            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
8002        } else {
8003            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
8004            match state
8005                .transitions()
8006                .first()
8007                .expect("single-transition path requires one transition")
8008                .data()
8009            {
8010                Transition::Rule { .. } => {
8011                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
8012                }
8013                Transition::Atom { .. }
8014                | Transition::Range { .. }
8015                | Transition::Set { .. }
8016                | Transition::NotSet { .. }
8017                | Transition::Wildcard { .. } => {
8018                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
8019                }
8020                _ => {
8021                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
8022                }
8023            }
8024        }
8025        let has_inserted_cycle_guard = if needs_cycle_guard {
8026            if !visiting.insert(key.clone()) {
8027                #[cfg(feature = "perf-counters")]
8028                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
8029                return Vec::new();
8030            }
8031            true
8032        } else {
8033            false
8034        };
8035        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
8036            Some(index)
8037        } else {
8038            decision_start_index
8039        };
8040        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
8041            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
8042        } else {
8043            (Rc::clone(&recovery_symbols), recovery_state)
8044        };
8045
8046        // Lookahead-based pruning. At a multi-alternative state we cache the
8047        // look-1 set of every outgoing transition; on visit we keep only the
8048        // transitions whose look-1 can accept the current lookahead (or that
8049        // can be reached without consuming and so could legitimately match a
8050        // shorter input). This is the main speedup vs. blind speculative
8051        // recursion: it lets each visit fan out only to the alternatives that
8052        // could possibly contribute a clean parse, mirroring the SLL phase of
8053        // ANTLR's adaptive prediction.
8054        //
8055        // Pruning is skipped at:
8056        //   * rule-start states (a child rule call may need every internal
8057        //     transition to surface single-token recovery diagnostics that
8058        //     ANTLR's reference parser emits at the rule's first consuming
8059        //     transition; the FIRST-set retry path turns the prefilter off
8060        //     entirely so let's keep this lightweight too),
8061        //   * left-recursive precedence loops (the precedence transition's
8062        //     gating is dynamic),
8063        //   * states with too few alternatives to benefit.
8064        let lookahead_filter = if transition_count > 1
8065            && self.fast_first_set_prefilter
8066            && !state.precedence_rule_decision()
8067            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
8068        {
8069            state
8070                .rule_index()
8071                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8072                .map(|rule_stop| {
8073                    let symbol = self.token_type_at(index);
8074                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
8075                    (symbol, entry)
8076                })
8077        } else {
8078            None
8079        };
8080        // LL(1) fast path: when the FIRST sets for the decision are disjoint
8081        // and none is nullable, the lookahead deterministically selects one
8082        // alternative. The recursive recognizer can then commit to that single
8083        // alt without iterating every transition through `should_skip_via_lookahead`
8084        // — saving (transition_count - 1) filter probes per visit.
8085        //
8086        // Result is cached per `(state, lookahead_token)` on the parser
8087        // instance, so subsequent visits skip the FIRST-set scan entirely.
8088        let ll1_only_alt: Option<usize> = if transition_count > 1
8089            && let Some((symbol, entry)) = lookahead_filter.as_ref()
8090        {
8091            let key = (state.state_number(), *symbol);
8092            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
8093                cached
8094            } else {
8095                let result = ll1_unique_alt(entry, *symbol);
8096                self.ll1_decision_cache.insert(key, result);
8097                result
8098            }
8099        } else {
8100            None
8101        };
8102        let lookahead_filter = lookahead_filter.as_ref();
8103        // Pre-size only when we expect at least one outcome to land — most
8104        // single-transition fall-throughs (the loop above didn't catch
8105        // because they're atom/rule/predicate) push at most one entry, so
8106        // reserving one slot avoids a reallocation while keeping the
8107        // unused-slot waste at one element.
8108        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
8109        for (transition_index, transition) in transitions.iter().enumerate() {
8110            if let Some(alt) = ll1_only_alt {
8111                // LL(1) determinism: skip every alt except the chosen one.
8112                if alt != transition_index {
8113                    continue;
8114                }
8115            }
8116            let transition_kind = transition.kind();
8117            if ll1_only_alt.is_none()
8118                && should_skip_via_lookahead(
8119                    transition_kind,
8120                    transition_index,
8121                    lookahead_filter,
8122                    index,
8123                    self.fast_recovery_enabled,
8124                    expected,
8125                )
8126            {
8127                continue;
8128            }
8129            let target = transition.target();
8130            match transition_kind {
8131                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
8132                    #[cfg(feature = "perf-counters")]
8133                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8134                    let boundary = left_recursive_boundary(atn, state, target);
8135                    outcomes.extend(
8136                        self.recognize_state_fast(
8137                            atn,
8138                            FastRecognizeRequest {
8139                                state_number: target,
8140                                stop_state,
8141                                index,
8142                                rule_start_index,
8143                                decision_start_index: next_decision_start_index,
8144                                precedence,
8145                                depth: depth + 1,
8146                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8147                                recovery_state: epsilon_recovery_state,
8148                            },
8149                            FastRecognizeScratch {
8150                                predicate_context,
8151                                visiting,
8152                                memo,
8153                                expected,
8154                            },
8155                        )
8156                        .into_iter()
8157                        .map(|mut outcome| {
8158                            if let Some(rule_index) = boundary {
8159                                let boundary = self.arena_boundary_node(rule_index);
8160                                self.defer_fast_outcome_node(&mut outcome, boundary);
8161                            }
8162                            outcome
8163                        }),
8164                    );
8165                }
8166                ParserTransitionKind::Predicate => {
8167                    #[cfg(feature = "perf-counters")]
8168                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8169                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
8170                        let boundary = left_recursive_boundary(atn, state, target);
8171                        outcomes.extend(
8172                            self.recognize_state_fast(
8173                                atn,
8174                                FastRecognizeRequest {
8175                                    state_number: target,
8176                                    stop_state,
8177                                    index,
8178                                    rule_start_index,
8179                                    decision_start_index: next_decision_start_index,
8180                                    precedence,
8181                                    depth: depth + 1,
8182                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8183                                    recovery_state: epsilon_recovery_state,
8184                                },
8185                                FastRecognizeScratch {
8186                                    predicate_context,
8187                                    visiting,
8188                                    memo,
8189                                    expected,
8190                                },
8191                            )
8192                            .into_iter()
8193                            .map(|mut outcome| {
8194                                if let Some(rule_index) = boundary {
8195                                    let boundary = self.arena_boundary_node(rule_index);
8196                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8197                                }
8198                                outcome
8199                            }),
8200                        );
8201                    } else {
8202                        record_predicate_no_viable(expected, next_decision_start_index, index);
8203                    }
8204                }
8205                ParserTransitionKind::Precedence => {
8206                    let transition_precedence = packed_i32(transition.arg0());
8207                    if transition_precedence >= precedence {
8208                        let boundary = left_recursive_boundary(atn, state, target);
8209                        outcomes.extend(
8210                            self.recognize_state_fast(
8211                                atn,
8212                                FastRecognizeRequest {
8213                                    state_number: target,
8214                                    stop_state,
8215                                    index,
8216                                    rule_start_index,
8217                                    decision_start_index: next_decision_start_index,
8218                                    precedence,
8219                                    depth: depth + 1,
8220                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8221                                    recovery_state: epsilon_recovery_state,
8222                                },
8223                                FastRecognizeScratch {
8224                                    predicate_context,
8225                                    visiting,
8226                                    memo,
8227                                    expected,
8228                                },
8229                            )
8230                            .into_iter()
8231                            .map(|mut outcome| {
8232                                if let Some(rule_index) = boundary {
8233                                    let boundary = self.arena_boundary_node(rule_index);
8234                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8235                                }
8236                                outcome
8237                            }),
8238                        );
8239                    }
8240                }
8241                ParserTransitionKind::Rule => {
8242                    let rule_index = transition.arg0() as usize;
8243                    let follow_state = transition.arg1() as usize;
8244                    let rule_precedence = packed_i32(transition.arg2());
8245                    #[cfg(feature = "perf-counters")]
8246                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
8247                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
8248                        continue;
8249                    };
8250                    // Lookahead-based pruning. The recognizer would otherwise
8251                    // explore every speculative rule call, producing exponential
8252                    // work on grammars with many epsilon-reachable rules. When
8253                    // the rule is non-nullable and its FIRST set excludes the
8254                    // current lookahead, recursion can't find a clean path
8255                    // *through this rule*. Skipping is only safe if some sibling
8256                    // transition can still consume the lookahead — otherwise the
8257                    // rule call is the sole continuation and must run so the
8258                    // single-token insertion / deletion recovery inside the
8259                    // called rule can fire (mirroring ANTLR's reference behavior
8260                    // of conjuring a missing token at child-rule entry).
8261                    let symbol = self.token_type_at(index);
8262                    if self.fast_first_set_prefilter {
8263                        // Probe the shared cross-parse cache first; build
8264                        // the entry on miss and intern it there. The
8265                        // computation is purely a function of the ATN, so
8266                        // the cached entry is reused across parses (and
8267                        // freshly-instantiated parser values that share
8268                        // the same `&'static Atn`).
8269                        //
8270                        // `rule_first_set` returns the computed entry
8271                        // directly — it intentionally skips inserting into
8272                        // the cache when the FIRST-set walk hit a cycle, so
8273                        // we cannot assume the entry is in the cache after
8274                        // computing it.
8275                        let first = self.cached_rule_first_set(atn, target, child_stop);
8276                        if should_skip_rule_via_first_set(
8277                            &first,
8278                            symbol,
8279                            self.fast_recovery_enabled,
8280                            index,
8281                            expected,
8282                        ) {
8283                            continue;
8284                        }
8285                    }
8286                    let expected_before_child =
8287                        self.fast_recovery_enabled.then(|| expected.clone());
8288                    let mut children = self.recognize_state_fast(
8289                        atn,
8290                        FastRecognizeRequest {
8291                            state_number: target,
8292                            stop_state: child_stop,
8293                            index,
8294                            rule_start_index: index,
8295                            decision_start_index: None,
8296                            precedence: rule_precedence,
8297                            depth: depth + 1,
8298                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8299                            recovery_state: epsilon_recovery_state,
8300                        },
8301                        FastRecognizeScratch {
8302                            predicate_context,
8303                            visiting,
8304                            memo,
8305                            expected,
8306                        },
8307                    );
8308                    if children.is_empty() && self.fast_recovery_enabled {
8309                        children = self.fast_child_rule_failure_recovery_outcomes(
8310                            FastChildRuleFailureRecoveryRequest {
8311                                atn,
8312                                rule_index,
8313                                start_index: index,
8314                                follow_state,
8315                                stop_state,
8316                                expected,
8317                            },
8318                        );
8319                    }
8320                    if let Some(expected_before_child) = expected_before_child {
8321                        if children
8322                            .iter()
8323                            .any(|child| child.diagnostics.is_empty() && child.index > index)
8324                        {
8325                            *expected = expected_before_child;
8326                        }
8327                    }
8328                    for child in children {
8329                        let child_index = child.index;
8330                        let child_consumed_eof = child.consumed_eof;
8331                        let child_diagnostics = child.diagnostics;
8332                        let empty_recovery = self.empty_recovery_symbols();
8333                        let follow_outcomes = self.recognize_state_fast(
8334                            atn,
8335                            FastRecognizeRequest {
8336                                state_number: follow_state,
8337                                stop_state,
8338                                index: child_index,
8339                                rule_start_index,
8340                                decision_start_index: next_decision_start_index,
8341                                precedence,
8342                                depth: depth + 1,
8343                                recovery_symbols: empty_recovery,
8344                                recovery_state: None,
8345                            },
8346                            FastRecognizeScratch {
8347                                predicate_context,
8348                                visiting,
8349                                memo,
8350                                expected,
8351                            },
8352                        );
8353                        if follow_outcomes.is_empty() {
8354                            continue;
8355                        }
8356                        let child_stop_index =
8357                            self.rule_stop_token_index(child_index, child_consumed_eof);
8358                        let child_node =
8359                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
8360                                rule_index: u32::try_from(rule_index)
8361                                    .expect("rule index fits in u32"),
8362                                invoking_state: i32::try_from(invoking_state_number(state_number))
8363                                    .expect("invoking state fits in i32"),
8364                                start_index: u32::try_from(index)
8365                                    .expect("rule start index fits in u32"),
8366                                stop_index: child_stop_index.map(|stop_index| {
8367                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
8368                                }),
8369                                deferred_children: child.deferred_nodes,
8370                                children: child.nodes,
8371                            });
8372                        let child_diags_empty = child_diagnostics.is_empty();
8373                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
8374                            outcome.consumed_eof |= child_consumed_eof;
8375                            // Skip the prepend dance when there's nothing to
8376                            // merge from the child — common case in pass 1.
8377                            if !child_diags_empty {
8378                                outcome.diagnostics = self
8379                                    .recognition_arena
8380                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
8381                            }
8382                            outcome.deferred_nodes = self
8383                                .recognition_arena
8384                                .concat_deferred_nodes(child_node, outcome.deferred_nodes);
8385                            outcome
8386                        }));
8387                    }
8388                }
8389                ParserTransitionKind::Atom
8390                | ParserTransitionKind::Range
8391                | ParserTransitionKind::Set
8392                | ParserTransitionKind::NotSet
8393                | ParserTransitionKind::Wildcard => {
8394                    #[cfg(feature = "perf-counters")]
8395                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8396                    let symbol = self.token_type_at(index);
8397                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8398                        let next_index = self.consume_index(index, symbol);
8399                        let empty_recovery = self.empty_recovery_symbols();
8400                        outcomes.extend(
8401                            self.recognize_state_fast(
8402                                atn,
8403                                FastRecognizeRequest {
8404                                    state_number: target,
8405                                    stop_state,
8406                                    index: next_index,
8407                                    rule_start_index,
8408                                    decision_start_index: next_decision_start_index,
8409                                    precedence,
8410                                    depth: depth + 1,
8411                                    recovery_symbols: empty_recovery,
8412                                    recovery_state: None,
8413                                },
8414                                FastRecognizeScratch {
8415                                    predicate_context,
8416                                    visiting,
8417                                    memo,
8418                                    expected,
8419                                },
8420                            )
8421                            .into_iter()
8422                            .map(|mut outcome| {
8423                                outcome.consumed_eof |= symbol == TOKEN_EOF;
8424                                if self.fast_token_nodes_enabled {
8425                                    let token = self.arena_token_node(index, false);
8426                                    self.defer_fast_outcome_node(&mut outcome, token);
8427                                }
8428                                outcome
8429                            }),
8430                        );
8431                    } else {
8432                        if !self.fast_recovery_enabled {
8433                            // In pass 1 there is no recovery to attempt; the
8434                            // recovery branch below would never run, and the
8435                            // `expected_symbols` computation is just there
8436                            // to gate that branch. Skipping it eliminates
8437                            // ~1× `state_expected_symbols` lookup per failed
8438                            // atom transition (≈82K on mono-statement.cs)
8439                            // for zero observable behavior change.
8440                            continue;
8441                        }
8442                        let expected_symbols = fast_recovery_expected_symbols(
8443                            self,
8444                            atn,
8445                            state.state_number(),
8446                            &recovery_symbols,
8447                        );
8448                        if expected_symbols.contains(&symbol) {
8449                            continue;
8450                        }
8451                        {
8452                            expected.record_transition(index, transition, max_token_type);
8453                            record_no_viable_if_ambiguous(
8454                                expected,
8455                                next_decision_start_index,
8456                                index,
8457                            );
8458                            outcomes.extend(self.fast_single_token_deletion_recovery(
8459                                FastRecoveryRequest {
8460                                    atn,
8461                                    transition,
8462                                    expected_symbols: Rc::clone(&expected_symbols),
8463                                    target,
8464                                    request: FastRecognizeRequest {
8465                                        state_number,
8466                                        stop_state,
8467                                        index,
8468                                        rule_start_index,
8469                                        decision_start_index,
8470                                        precedence,
8471                                        depth,
8472                                        recovery_symbols: Rc::clone(&recovery_symbols),
8473                                        recovery_state,
8474                                    },
8475                                    visiting,
8476                                    memo,
8477                                    expected,
8478                                },
8479                                predicate_context,
8480                            ));
8481                            if !state_is_left_recursive_rule(atn, state) {
8482                                outcomes.extend(self.fast_single_token_insertion_recovery(
8483                                    FastRecoveryRequest {
8484                                        atn,
8485                                        transition,
8486                                        expected_symbols: Rc::clone(&expected_symbols),
8487                                        target,
8488                                        request: FastRecognizeRequest {
8489                                            state_number,
8490                                            stop_state,
8491                                            index,
8492                                            rule_start_index,
8493                                            decision_start_index,
8494                                            precedence,
8495                                            depth,
8496                                            recovery_symbols: Rc::clone(&recovery_symbols),
8497                                            recovery_state,
8498                                        },
8499                                        visiting,
8500                                        memo,
8501                                        expected,
8502                                    },
8503                                    predicate_context,
8504                                ));
8505                            }
8506                            outcomes.extend(self.fast_current_token_deletion_recovery(
8507                                FastCurrentTokenDeletionRequest {
8508                                    atn,
8509                                    expected_symbols,
8510                                    request: FastRecognizeRequest {
8511                                        state_number,
8512                                        stop_state,
8513                                        index,
8514                                        rule_start_index,
8515                                        decision_start_index,
8516                                        precedence,
8517                                        depth,
8518                                        recovery_symbols: Rc::clone(&recovery_symbols),
8519                                        recovery_state,
8520                                    },
8521                                    visiting,
8522                                    memo,
8523                                    expected,
8524                                },
8525                                predicate_context,
8526                            ));
8527                        }
8528                    }
8529                }
8530            }
8531        }
8532
8533        if has_inserted_cycle_guard {
8534            visiting.remove(&key);
8535        }
8536        if matches!(
8537            self.prediction_mode,
8538            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
8539        ) && self.fast_recovery_enabled
8540        {
8541            // Without recovery enabled every outcome already has empty
8542            // diagnostics, so the discard pass is a no-op — skipping it
8543            // saves an iter+retain on each of the ~1M visits.
8544            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
8545        }
8546        if self.fast_recovery_enabled {
8547            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
8548        } else {
8549            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8550        }
8551        // Skip memoization for single-transition states whose outcome is
8552        // unambiguous: they only get re-entered if the caller revisits the
8553        // exact same call site, which is rare since the loop above already
8554        // collapsed straight-line epsilon walks. Multi-alternative states
8555        // are where backtracking actually revisits the same coordinate, so
8556        // we still memoize there. With recovery on we keep the existing
8557        // memoization unconditionally because the recovery branch may
8558        // record diagnostics that the cache must surface to repeated
8559        // failed visits.
8560        let should_memoize = self.fast_recovery_enabled
8561            || (transition_count > 1
8562                && (outcomes.is_empty()
8563                    || outcomes.len() > 1
8564                    || (outcomes.len() == 1 && self.should_memoize_single_outcome(&key))));
8565        // Apply inline pending state to each outcome before returning.
8566        // Tokens consumed inline by the loop-collapse don't appear in the
8567        // recursive recognizer's output, so we need to prepend them here.
8568        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
8569            if inline_consumed_eof {
8570                outcome.consumed_eof = true;
8571            }
8572            if !inline_consumed_tokens.is_empty() {
8573                for token_index in inline_consumed_tokens.iter().rev() {
8574                    let token = self.arena_token_node(*token_index, false);
8575                    self.defer_fast_outcome_node(&mut outcome, token);
8576                }
8577            }
8578            outcome
8579        };
8580        if should_memoize {
8581            #[cfg(feature = "perf-counters")]
8582            {
8583                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
8584                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
8585                match outcomes.len() {
8586                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8587                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8588                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8589                }
8590            }
8591            // The memo is keyed by the loop-exit `(state_number, index)` so
8592            // the inline-consumed tokens belong to *this* call's output, not
8593            // the cached result. Memoize the bare outcomes (without the
8594            // inline-pending data), then prepend the inline data on return.
8595            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
8596            memo.insert(key, Rc::clone(&stored));
8597            if inline_pending {
8598                return stored
8599                    .iter()
8600                    .copied()
8601                    .map(&mut apply_inline_pending)
8602                    .collect();
8603            }
8604            return stored.to_vec();
8605        }
8606        #[cfg(feature = "perf-counters")]
8607        match outcomes.len() {
8608            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8609            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8610            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8611        }
8612        if inline_pending {
8613            return outcomes.into_iter().map(apply_inline_pending).collect();
8614        }
8615        outcomes
8616    }
8617
8618    /// Explores single-token deletion recovery while preserving the matched
8619    /// token and skipped error token in the selected parse tree path.
8620    fn single_token_deletion_recovery(
8621        &mut self,
8622        recovery: RecoveryRequest<'_, '_>,
8623    ) -> Vec<RecognizeOutcome> {
8624        let RecoveryRequest {
8625            atn,
8626            transition,
8627            expected_symbols,
8628            target,
8629            request,
8630            visiting,
8631            memo,
8632            expected,
8633        } = recovery;
8634        let RecognizeRequest {
8635            stop_state,
8636            index,
8637            rule_start_index,
8638            decision_start_index,
8639            init_action_rules,
8640            predicates,
8641            semantics,
8642            rule_args,
8643            member_actions,
8644            return_actions,
8645            local_int_arg,
8646            member_values,
8647            return_values,
8648            rule_alt_number,
8649            track_alt_numbers,
8650            consumed_eof,
8651            precedence,
8652            depth,
8653            ..
8654        } = request;
8655        let Some((diagnostic, next_index, next_symbol)) =
8656            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8657        else {
8658            return Vec::new();
8659        };
8660        let after_next = self.consume_index(next_index, next_symbol);
8661        self.recognize_state(
8662            atn,
8663            RecognizeRequest {
8664                state_number: target,
8665                stop_state,
8666                index: after_next,
8667                rule_start_index,
8668                decision_start_index,
8669                init_action_rules,
8670                predicates,
8671                semantics,
8672                rule_args,
8673                member_actions,
8674                return_actions,
8675                local_int_arg,
8676                member_values,
8677                return_values,
8678                rule_alt_number,
8679                track_alt_numbers,
8680                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
8681                precedence,
8682                depth: depth + 1,
8683                recovery_symbols: BTreeSet::new(),
8684                recovery_state: None,
8685            },
8686            visiting,
8687            memo,
8688            expected,
8689        )
8690        .into_iter()
8691        .map(|mut outcome| {
8692            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
8693            outcome.diagnostics = self
8694                .recognition_arena
8695                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8696            let token = self.arena_token_node(next_index, false);
8697            self.arena_prepend(&mut outcome.nodes, token);
8698            let error = self.arena_token_node(index, true);
8699            self.arena_prepend(&mut outcome.nodes, error);
8700            outcome
8701        })
8702        .collect()
8703    }
8704
8705    /// Retries the current recognition state after deleting one unexpected
8706    /// token, preserving the deleted token as an error node in the parse tree.
8707    fn current_token_deletion_recovery(
8708        &mut self,
8709        recovery: CurrentTokenDeletionRequest<'_, '_>,
8710    ) -> Vec<RecognizeOutcome> {
8711        let CurrentTokenDeletionRequest {
8712            atn,
8713            expected_symbols,
8714            mut request,
8715            visiting,
8716            memo,
8717            expected,
8718        } = recovery;
8719        let error_index = request.index;
8720        if error_index == request.rule_start_index {
8721            return Vec::new();
8722        }
8723        let Some((diagnostic, next_index, skipped)) =
8724            self.current_token_deletion(error_index, &expected_symbols)
8725        else {
8726            return Vec::new();
8727        };
8728        request.state_number = request.recovery_state.unwrap_or(request.state_number);
8729        request.index = next_index;
8730        request.depth += 1;
8731        request.recovery_state = None;
8732        self.recognize_state(atn, request, visiting, memo, expected)
8733            .into_iter()
8734            .map(|mut outcome| {
8735                outcome.diagnostics = self
8736                    .recognition_arena
8737                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8738                for index in skipped.iter().rev() {
8739                    let error = self.arena_token_node(*index, true);
8740                    self.arena_prepend(&mut outcome.nodes, error);
8741                }
8742                outcome
8743            })
8744            .collect()
8745    }
8746
8747    /// Falls back after deletion/insertion repairs cannot continue from a
8748    /// failed consuming transition.
8749    fn consuming_failure_fallback(
8750        &mut self,
8751        fallback: ConsumingFailureFallback<'_>,
8752        visiting: &mut BTreeSet<RecognizeKey>,
8753        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
8754        expected: &mut ExpectedTokens,
8755    ) -> Vec<RecognizeOutcome> {
8756        if fallback.expected_symbols.is_empty() {
8757            return Vec::new();
8758        }
8759        if fallback.symbol == TOKEN_EOF {
8760            return self.eof_consuming_failure_fallback(fallback, expected);
8761        }
8762        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
8763    }
8764
8765    /// Keeps unexpected non-EOF input visible as an error node when no repair
8766    /// path can otherwise reach the transition target.
8767    fn non_eof_consuming_failure_fallback(
8768        &mut self,
8769        fallback: ConsumingFailureFallback<'_>,
8770        visiting: &mut BTreeSet<RecognizeKey>,
8771        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
8772        expected: &mut ExpectedTokens,
8773    ) -> Vec<RecognizeOutcome> {
8774        let ConsumingFailureFallback {
8775            atn,
8776            target,
8777            request,
8778            symbol,
8779            expected_symbols,
8780            decision_start_index,
8781            decision,
8782        } = fallback;
8783        let error_index = request.index;
8784        let diagnostic =
8785            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
8786        let next_index = self.consume_index(error_index, symbol);
8787        self.recognize_state(
8788            atn,
8789            RecognizeRequest {
8790                state_number: target,
8791                stop_state: request.stop_state,
8792                index: next_index,
8793                rule_start_index: request.rule_start_index,
8794                decision_start_index,
8795                init_action_rules: request.init_action_rules,
8796                predicates: request.predicates,
8797                semantics: request.semantics,
8798                rule_args: request.rule_args,
8799                member_actions: request.member_actions,
8800                return_actions: request.return_actions,
8801                local_int_arg: request.local_int_arg,
8802                member_values: request.member_values,
8803                return_values: request.return_values,
8804                rule_alt_number: request.rule_alt_number,
8805                track_alt_numbers: request.track_alt_numbers,
8806                consumed_eof: request.consumed_eof,
8807                precedence: request.precedence,
8808                depth: request.depth + 1,
8809                recovery_symbols: BTreeSet::new(),
8810                recovery_state: None,
8811            },
8812            visiting,
8813            memo,
8814            expected,
8815        )
8816        .into_iter()
8817        .map(|mut outcome| {
8818            prepend_decision(&mut outcome, decision);
8819            outcome.diagnostics = self
8820                .recognition_arena
8821                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8822            let error = self.arena_token_node(error_index, true);
8823            self.arena_prepend(&mut outcome.nodes, error);
8824            outcome
8825        })
8826        .collect()
8827    }
8828
8829    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
8830    /// behavior of unwinding instead of inserting caller tokens at EOF.
8831    fn eof_consuming_failure_fallback(
8832        &mut self,
8833        fallback: ConsumingFailureFallback<'_>,
8834        expected: &ExpectedTokens,
8835    ) -> Vec<RecognizeOutcome> {
8836        let request = fallback.request;
8837        if request.index == request.rule_start_index {
8838            return Vec::new();
8839        }
8840        let diagnostic =
8841            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
8842        let diagnostics = self
8843            .recognition_arena
8844            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8845        vec![RecognizeOutcome {
8846            index: request.index,
8847            consumed_eof: request.consumed_eof,
8848            alt_number: request.rule_alt_number,
8849            member_values: request.member_values,
8850            return_values: request.return_values,
8851            diagnostics,
8852            decisions: Vec::new(),
8853            actions: Vec::new(),
8854            nodes: NodeSeqId::EMPTY,
8855        }]
8856    }
8857
8858    /// Explores single-token insertion recovery while adding a conjured
8859    /// missing-token error node to the selected parse tree path.
8860    fn single_token_insertion_recovery(
8861        &mut self,
8862        recovery: RecoveryRequest<'_, '_>,
8863    ) -> Vec<RecognizeOutcome> {
8864        let RecoveryRequest {
8865            atn,
8866            transition,
8867            expected_symbols,
8868            target,
8869            request,
8870            visiting,
8871            memo,
8872            expected,
8873        } = recovery;
8874        let RecognizeRequest {
8875            stop_state,
8876            index,
8877            rule_start_index,
8878            decision_start_index,
8879            init_action_rules,
8880            predicates,
8881            semantics,
8882            rule_args,
8883            member_actions,
8884            return_actions,
8885            local_int_arg,
8886            member_values,
8887            return_values,
8888            rule_alt_number,
8889            track_alt_numbers,
8890            consumed_eof,
8891            precedence,
8892            depth,
8893            ..
8894        } = request;
8895        let follow_symbols = state_expected_symbols(atn, transition.target());
8896        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
8897            transition,
8898            index,
8899            atn.max_token_type(),
8900            &expected_symbols,
8901            &follow_symbols,
8902        ) else {
8903            return Vec::new();
8904        };
8905        self.recognize_state(
8906            atn,
8907            RecognizeRequest {
8908                state_number: target,
8909                stop_state,
8910                index,
8911                rule_start_index,
8912                decision_start_index,
8913                init_action_rules,
8914                predicates,
8915                semantics,
8916                rule_args,
8917                member_actions,
8918                return_actions,
8919                local_int_arg,
8920                member_values,
8921                return_values,
8922                rule_alt_number,
8923                track_alt_numbers,
8924                consumed_eof,
8925                precedence,
8926                depth: depth + 1,
8927                recovery_symbols: BTreeSet::new(),
8928                recovery_state: None,
8929            },
8930            visiting,
8931            memo,
8932            expected,
8933        )
8934        .into_iter()
8935        .map(|mut outcome| {
8936            outcome.diagnostics = self
8937                .recognition_arena
8938                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8939            let missing = self.arena_missing_token_node(token_type, index, text.clone());
8940            self.arena_prepend(&mut outcome.nodes, missing);
8941            outcome
8942        })
8943        .collect()
8944    }
8945
8946    /// Attempts to reach `stop_state` and carries semantic actions for the
8947    /// selected parser path.
8948    #[allow(clippy::too_many_lines)]
8949    fn recognize_state(
8950        &mut self,
8951        atn: &Atn,
8952        request: RecognizeRequest<'_>,
8953        visiting: &mut BTreeSet<RecognizeKey>,
8954        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
8955        expected: &mut ExpectedTokens,
8956    ) -> Vec<RecognizeOutcome> {
8957        let request_template = request.clone();
8958        let RecognizeRequest {
8959            state_number,
8960            stop_state,
8961            index,
8962            rule_start_index,
8963            decision_start_index,
8964            init_action_rules,
8965            predicates,
8966            semantics,
8967            rule_args,
8968            member_actions,
8969            return_actions,
8970            local_int_arg,
8971            member_values,
8972            return_values,
8973            rule_alt_number,
8974            track_alt_numbers,
8975            consumed_eof,
8976            precedence,
8977            depth,
8978            recovery_symbols,
8979            recovery_state,
8980        } = request;
8981        if depth > RECOGNITION_DEPTH_LIMIT {
8982            return Vec::new();
8983        }
8984        if state_number == stop_state {
8985            return stop_outcome(
8986                index,
8987                consumed_eof,
8988                rule_alt_number,
8989                member_values,
8990                return_values,
8991            );
8992        }
8993        let key = RecognizeKey {
8994            state_number,
8995            stop_state,
8996            index,
8997            rule_start_index,
8998            decision_start_index,
8999            local_int_arg,
9000            member_values: member_values.clone(),
9001            return_values: return_values.clone(),
9002            rule_alt_number,
9003            track_alt_numbers,
9004            consumed_eof,
9005            precedence,
9006            recovery_symbols: recovery_symbols.clone(),
9007            recovery_state,
9008        };
9009        if let Some(outcomes) = memo.get(&key) {
9010            return outcomes.clone();
9011        }
9012
9013        let visit_key = key.clone();
9014        if !visiting.insert(visit_key.clone()) {
9015            return Vec::new();
9016        }
9017
9018        let Some(state) = atn.state(state_number) else {
9019            visiting.remove(&visit_key);
9020            return Vec::new();
9021        };
9022        let transitions = state.transitions();
9023        let transition_count = transitions.len();
9024        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9025            Some(index)
9026        } else {
9027            decision_start_index
9028        };
9029        let (epsilon_recovery_symbols, epsilon_recovery_state) =
9030            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
9031        let mut outcomes = Vec::new();
9032        for (transition_index, transition) in transitions.iter().enumerate() {
9033            let decision =
9034                transition_decision(atn, state, transition_count, transition_index, predicates);
9035            let next_alt_number = next_alt_number(
9036                state,
9037                transition_count,
9038                transition_index,
9039                rule_alt_number,
9040                track_alt_numbers,
9041            );
9042            let transition_data = transition.data();
9043            match &transition_data {
9044                Transition::Epsilon { target } | Transition::Action { target, .. } => {
9045                    let action_rule_index = match &transition_data {
9046                        Transition::Action { rule_index, .. } => Some(*rule_index),
9047                        _ => None,
9048                    };
9049                    outcomes.extend(self.recognize_epsilon_or_action_step(
9050                        atn,
9051                        &request_template,
9052                        EpsilonActionStep {
9053                            source_state: state_number,
9054                            target: *target,
9055                            action_rule_index,
9056                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
9057                            decision,
9058                            decision_start_index: next_decision_start_index,
9059                            alt_number: next_alt_number,
9060                            recovery_symbols: epsilon_recovery_symbols.clone(),
9061                            recovery_state: epsilon_recovery_state,
9062                        },
9063                        RecognizeScratch {
9064                            visiting,
9065                            memo,
9066                            expected,
9067                        },
9068                    ));
9069                }
9070                Transition::Predicate {
9071                    target,
9072                    rule_index,
9073                    pred_index,
9074                    ..
9075                } => {
9076                    let predicate = PredicateEval {
9077                        index,
9078                        rule_index: *rule_index,
9079                        pred_index: *pred_index,
9080                        predicates,
9081                        semantics,
9082                        context: None,
9083                        local_int_arg,
9084                        member_values: &member_values,
9085                    };
9086                    if self.parser_predicate_matches(predicate) {
9087                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
9088                        outcomes.extend(
9089                            self.recognize_state(
9090                                atn,
9091                                RecognizeRequest {
9092                                    state_number: *target,
9093                                    stop_state,
9094                                    index,
9095                                    rule_start_index,
9096                                    decision_start_index: next_decision_start_index,
9097                                    init_action_rules,
9098                                    predicates,
9099                                    semantics,
9100                                    rule_args,
9101                                    member_actions,
9102                                    return_actions,
9103                                    local_int_arg,
9104                                    member_values: member_values.clone(),
9105                                    return_values: return_values.clone(),
9106                                    rule_alt_number: next_alt_number,
9107                                    track_alt_numbers,
9108                                    consumed_eof,
9109                                    precedence,
9110                                    depth: depth + 1,
9111                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9112                                    recovery_state: epsilon_recovery_state,
9113                                },
9114                                visiting,
9115                                memo,
9116                                expected,
9117                            )
9118                            .into_iter()
9119                            .map(|mut outcome| {
9120                                prepend_decision(&mut outcome, decision);
9121                                if let Some(rule_index) = left_recursive_boundary {
9122                                    let boundary = self.arena_boundary_node(rule_index);
9123                                    self.arena_prepend(&mut outcome.nodes, boundary);
9124                                }
9125                                outcome
9126                            }),
9127                        );
9128                    } else if let Some(message) = semantics
9129                        .and_then(|semantics| {
9130                            self.parser_semantic_ir_predicate_failure_message(
9131                                *rule_index,
9132                                *pred_index,
9133                                semantics,
9134                            )
9135                        })
9136                        .or_else(|| {
9137                            self.parser_predicate_failure_message(
9138                                *rule_index,
9139                                *pred_index,
9140                                predicates,
9141                            )
9142                        })
9143                    {
9144                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
9145                            rule_index: *rule_index,
9146                            index,
9147                            message,
9148                            member_values: member_values.clone(),
9149                            return_values: return_values.clone(),
9150                            rule_alt_number,
9151                        }));
9152                    } else {
9153                        record_predicate_no_viable(expected, next_decision_start_index, index);
9154                    }
9155                }
9156                Transition::Precedence {
9157                    target,
9158                    precedence: transition_precedence,
9159                } => {
9160                    if *transition_precedence >= precedence {
9161                        outcomes.extend(
9162                            self.recognize_state(
9163                                atn,
9164                                RecognizeRequest {
9165                                    state_number: *target,
9166                                    stop_state,
9167                                    index,
9168                                    rule_start_index,
9169                                    decision_start_index: next_decision_start_index,
9170                                    init_action_rules,
9171                                    predicates,
9172                                    semantics,
9173                                    rule_args,
9174                                    member_actions,
9175                                    return_actions,
9176                                    local_int_arg,
9177                                    member_values: member_values.clone(),
9178                                    return_values: return_values.clone(),
9179                                    rule_alt_number: next_alt_number,
9180                                    track_alt_numbers,
9181                                    consumed_eof,
9182                                    precedence,
9183                                    depth: depth + 1,
9184                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9185                                    recovery_state: epsilon_recovery_state,
9186                                },
9187                                visiting,
9188                                memo,
9189                                expected,
9190                            )
9191                            .into_iter()
9192                            .map(|mut outcome| {
9193                                prepend_decision(&mut outcome, decision);
9194                                outcome
9195                            }),
9196                        );
9197                    }
9198                }
9199                Transition::Rule {
9200                    target,
9201                    rule_index,
9202                    follow_state,
9203                    precedence: rule_precedence,
9204                    ..
9205                } => {
9206                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
9207                        continue;
9208                    };
9209                    let child_local_int_arg =
9210                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
9211                    let expected_before_child = expected.clone();
9212                    let children = self.recognize_state(
9213                        atn,
9214                        RecognizeRequest {
9215                            state_number: *target,
9216                            stop_state: child_stop,
9217                            index,
9218                            rule_start_index: index,
9219                            decision_start_index: None,
9220                            init_action_rules,
9221                            predicates,
9222                            semantics,
9223                            rule_args,
9224                            member_actions,
9225                            return_actions,
9226                            local_int_arg: child_local_int_arg,
9227                            member_values: member_values.clone(),
9228                            return_values: BTreeMap::new(),
9229                            rule_alt_number: 0,
9230                            track_alt_numbers,
9231                            consumed_eof: false,
9232                            precedence: *rule_precedence,
9233                            depth: depth + 1,
9234                            recovery_symbols: epsilon_recovery_symbols.clone(),
9235                            recovery_state: epsilon_recovery_state,
9236                        },
9237                        visiting,
9238                        memo,
9239                        expected,
9240                    );
9241                    let children = if children.is_empty() {
9242                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
9243                            atn,
9244                            rule_index: *rule_index,
9245                            start_index: index,
9246                            follow_state: *follow_state,
9247                            stop_state,
9248                            member_values: member_values.clone(),
9249                            expected,
9250                        })
9251                    } else {
9252                        children
9253                    };
9254                    let preserve_child_expected =
9255                        self.child_expected_reaches_clean_eof(&children, expected);
9256                    restore_expected(
9257                        &children,
9258                        index,
9259                        expected,
9260                        expected_before_child,
9261                        preserve_child_expected,
9262                    );
9263                    for child in children {
9264                        let child_stop_index =
9265                            self.rule_stop_token_index(child.index, child.consumed_eof);
9266                        let child_nodes = self
9267                            .recognition_arena
9268                            .fold_left_recursive_boundaries(child.nodes);
9269                        let child_node = self.arena_rule_node(ArenaRuleSpec {
9270                            rule_index: *rule_index,
9271                            invoking_state: invoking_state_number(state_number),
9272                            alt_number: child.alt_number,
9273                            start_index: index,
9274                            stop_index: child_stop_index,
9275                            return_values: child.return_values.clone(),
9276                            children: child_nodes,
9277                        });
9278                        outcomes.extend(
9279                            self.recognize_state(
9280                                atn,
9281                                RecognizeRequest {
9282                                    state_number: *follow_state,
9283                                    stop_state,
9284                                    index: child.index,
9285                                    rule_start_index,
9286                                    decision_start_index: next_decision_start_index,
9287                                    init_action_rules,
9288                                    predicates,
9289                                    semantics,
9290                                    rule_args,
9291                                    member_actions,
9292                                    return_actions,
9293                                    local_int_arg,
9294                                    member_values: child.member_values.clone(),
9295                                    return_values: return_values.clone(),
9296                                    rule_alt_number,
9297                                    track_alt_numbers,
9298                                    consumed_eof: consumed_eof || child.consumed_eof,
9299                                    precedence,
9300                                    depth: depth + 1,
9301                                    recovery_symbols: BTreeSet::new(),
9302                                    recovery_state: None,
9303                                },
9304                                visiting,
9305                                memo,
9306                                expected,
9307                            )
9308                            .into_iter()
9309                            .map(|mut outcome| {
9310                                outcome.consumed_eof |= child.consumed_eof;
9311                                outcome.diagnostics = self
9312                                    .recognition_arena
9313                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
9314                                let mut decisions = child.decisions.clone();
9315                                decisions.append(&mut outcome.decisions);
9316                                outcome.decisions = decisions;
9317                                prepend_decision(&mut outcome, decision);
9318                                let mut actions = child.actions.clone();
9319                                if init_action_rules.contains(rule_index) {
9320                                    actions.insert(
9321                                        0,
9322                                        ParserAction::new_rule_init(
9323                                            *rule_index,
9324                                            index,
9325                                            Some(*follow_state),
9326                                        ),
9327                                    );
9328                                }
9329                                actions.append(&mut outcome.actions);
9330                                outcome.actions = actions;
9331                                self.arena_prepend(&mut outcome.nodes, child_node);
9332                                outcome
9333                            }),
9334                        );
9335                    }
9336                }
9337                Transition::Atom { target, .. }
9338                | Transition::Range { target, .. }
9339                | Transition::Set { target, .. }
9340                | Transition::NotSet { target, .. }
9341                | Transition::Wildcard { target, .. } => {
9342                    let symbol = self.token_type_at(index);
9343                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
9344                        let next_index = self.consume_index(index, symbol);
9345                        outcomes.extend(
9346                            self.recognize_state(
9347                                atn,
9348                                RecognizeRequest {
9349                                    state_number: *target,
9350                                    stop_state,
9351                                    index: next_index,
9352                                    rule_start_index,
9353                                    decision_start_index: next_decision_start_index,
9354                                    init_action_rules,
9355                                    predicates,
9356                                    semantics,
9357                                    rule_args,
9358                                    member_actions,
9359                                    return_actions,
9360                                    local_int_arg,
9361                                    member_values: member_values.clone(),
9362                                    return_values: return_values.clone(),
9363                                    rule_alt_number: next_alt_number,
9364                                    track_alt_numbers,
9365                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
9366                                    precedence,
9367                                    depth: depth + 1,
9368                                    recovery_symbols: BTreeSet::new(),
9369                                    recovery_state: None,
9370                                },
9371                                visiting,
9372                                memo,
9373                                expected,
9374                            )
9375                            .into_iter()
9376                            .map(|mut outcome| {
9377                                prepend_decision(&mut outcome, decision);
9378                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9379                                let token = self.arena_token_node(index, false);
9380                                self.arena_prepend(&mut outcome.nodes, token);
9381                                outcome
9382                            }),
9383                        );
9384                    } else {
9385                        let expected_symbols =
9386                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
9387                        if expected_symbols.contains(&symbol) {
9388                            continue;
9389                        }
9390                        expected.record_transition(index, transition, atn.max_token_type());
9391                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
9392                        let before_recovery = outcomes.len();
9393                        let recovery_request = request_template.clone();
9394                        outcomes.extend(
9395                            self.single_token_deletion_recovery(RecoveryRequest {
9396                                atn,
9397                                transition,
9398                                expected_symbols: expected_symbols.clone(),
9399                                target: *target,
9400                                request: recovery_request.clone(),
9401                                visiting,
9402                                memo,
9403                                expected,
9404                            })
9405                            .into_iter()
9406                            .map(|mut outcome| {
9407                                prepend_decision(&mut outcome, decision);
9408                                outcome
9409                            }),
9410                        );
9411                        if !state_is_left_recursive_rule(atn, state) {
9412                            outcomes.extend(
9413                                self.single_token_insertion_recovery(RecoveryRequest {
9414                                    atn,
9415                                    transition,
9416                                    expected_symbols: expected_symbols.clone(),
9417                                    target: *target,
9418                                    request: recovery_request.clone(),
9419                                    visiting,
9420                                    memo,
9421                                    expected,
9422                                })
9423                                .into_iter()
9424                                .map(|mut outcome| {
9425                                    prepend_decision(&mut outcome, decision);
9426                                    outcome
9427                                }),
9428                            );
9429                        }
9430                        outcomes.extend(self.current_token_deletion_recovery(
9431                            CurrentTokenDeletionRequest {
9432                                atn,
9433                                expected_symbols: expected_symbols.clone(),
9434                                request: recovery_request.clone(),
9435                                visiting,
9436                                memo,
9437                                expected,
9438                            },
9439                        ));
9440                        if outcomes.len() == before_recovery {
9441                            outcomes.extend(self.consuming_failure_fallback(
9442                                ConsumingFailureFallback {
9443                                    atn,
9444                                    target: *target,
9445                                    request: recovery_request,
9446                                    symbol,
9447                                    expected_symbols,
9448                                    decision_start_index: next_decision_start_index,
9449                                    decision,
9450                                },
9451                                visiting,
9452                                memo,
9453                                expected,
9454                            ));
9455                        }
9456                    }
9457                }
9458            }
9459        }
9460
9461        visiting.remove(&visit_key);
9462        self.record_prediction_diagnostics(atn, state, index, &outcomes);
9463        if matches!(
9464            self.prediction_mode,
9465            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9466        ) {
9467            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
9468        }
9469        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
9470        memo.insert(key, outcomes.clone());
9471        outcomes
9472    }
9473
9474    /// Follows an epsilon or semantic-action transition while preserving the
9475    /// path-local side effects that may later become generated action output.
9476    fn recognize_epsilon_or_action_step(
9477        &mut self,
9478        atn: &Atn,
9479        request: &RecognizeRequest<'_>,
9480        step: EpsilonActionStep,
9481        scratch: RecognizeScratch<'_>,
9482    ) -> Vec<RecognizeOutcome> {
9483        let RecognizeScratch {
9484            visiting,
9485            memo,
9486            expected,
9487        } = scratch;
9488        let action = step.action_rule_index.map(|rule_index| {
9489            ParserAction::new(
9490                step.source_state,
9491                rule_index,
9492                request.rule_start_index,
9493                self.rule_stop_token_index(request.index, request.consumed_eof),
9494            )
9495        });
9496        let next_member_values = if action.is_some() {
9497            member_values_after_action(
9498                step.source_state,
9499                request.member_actions,
9500                request.semantics,
9501                &request.member_values,
9502            )
9503        } else {
9504            request.member_values.clone()
9505        };
9506        let next_return_values = action.map_or_else(
9507            || request.return_values.clone(),
9508            |action| {
9509                return_values_after_action(
9510                    step.source_state,
9511                    action.rule_index(),
9512                    request.return_actions,
9513                    request.semantics,
9514                    &request.return_values,
9515                )
9516            },
9517        );
9518
9519        self.recognize_state(
9520            atn,
9521            RecognizeRequest {
9522                state_number: step.target,
9523                stop_state: request.stop_state,
9524                index: request.index,
9525                rule_start_index: request.rule_start_index,
9526                decision_start_index: step.decision_start_index,
9527                init_action_rules: request.init_action_rules,
9528                predicates: request.predicates,
9529                semantics: request.semantics,
9530                rule_args: request.rule_args,
9531                member_actions: request.member_actions,
9532                return_actions: request.return_actions,
9533                local_int_arg: request.local_int_arg,
9534                member_values: next_member_values,
9535                return_values: next_return_values,
9536                rule_alt_number: step.alt_number,
9537                track_alt_numbers: request.track_alt_numbers,
9538                consumed_eof: request.consumed_eof,
9539                precedence: request.precedence,
9540                depth: request.depth + 1,
9541                recovery_symbols: step.recovery_symbols,
9542                recovery_state: step.recovery_state,
9543            },
9544            visiting,
9545            memo,
9546            expected,
9547        )
9548        .into_iter()
9549        .map(|mut outcome| {
9550            prepend_decision(&mut outcome, step.decision);
9551            if let Some(rule_index) = step.left_recursive_boundary {
9552                let boundary = self.arena_boundary_node(rule_index);
9553                self.arena_prepend(&mut outcome.nodes, boundary);
9554            }
9555            if let Some(action) = action {
9556                outcome.actions.insert(0, action);
9557            }
9558            outcome
9559        })
9560        .collect()
9561    }
9562
9563    /// Reads the token type at an absolute token-stream index without moving
9564    /// the parser's stream cursor. The fast recognizer probes lookahead at
9565    /// every state visit, so avoiding the seek round-trip is a measurable
9566    /// hot-path win on long inputs.
9567    fn token_type_at(&mut self, index: usize) -> i32 {
9568        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
9569            self.input.fill();
9570        }
9571        self.input.token_type_at_index(index)
9572    }
9573
9574    /// Returns the cached `state_expected_symbols` set for an ATN state.
9575    ///
9576    /// The fast recognizer consults this set on every state visit through
9577    /// `next_recovery_context`; the underlying DFS is a pure function of the
9578    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
9579    ///
9580    /// Caching is layered through `intern_recovery_symbols` so two ATN states
9581    /// with the same expected-symbol set share one `Rc`. That invariant is
9582    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
9583    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
9584    /// always interned before it ends up in a key.
9585    fn cached_state_expected_symbols(
9586        &mut self,
9587        atn: &Atn,
9588        state_number: usize,
9589    ) -> Rc<BTreeSet<i32>> {
9590        if let Some(cached) = self.state_expected_cache.get(&state_number) {
9591            return Rc::clone(cached);
9592        }
9593        let symbols = state_expected_symbols(atn, state_number);
9594        let entry = self.intern_recovery_symbols(symbols);
9595        self.state_expected_cache
9596            .insert(state_number, Rc::clone(&entry));
9597        entry
9598    }
9599
9600    fn cached_state_expected_token_set(
9601        &mut self,
9602        atn: &Atn,
9603        state_number: usize,
9604    ) -> Rc<TokenBitSet> {
9605        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
9606            return Rc::clone(cached);
9607        }
9608        // Purely a function of the ATN, so back the per-parser cache with the
9609        // thread-shared one — fresh parser instances (one per parse in
9610        // generated usage) start warm instead of rewalking the ATN.
9611        let symbols = with_shared_atn_caches(atn, |cache| {
9612            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
9613                return Rc::clone(cached);
9614            }
9615            let symbols = Rc::new(state_expected_token_set(atn, state_number));
9616            cache
9617                .state_expected_tokens
9618                .insert(state_number, Rc::clone(&symbols));
9619            symbols
9620        });
9621        self.state_expected_token_cache
9622            .insert(state_number, Rc::clone(&symbols));
9623        symbols
9624    }
9625
9626    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
9627        if self.rule_stop_reach_cache.len() <= state_number {
9628            self.rule_stop_reach_cache
9629                .resize_with(atn.states().len().max(state_number + 1), || None);
9630        }
9631        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
9632            return reaches;
9633        }
9634        let reaches = with_shared_atn_caches(atn, |cache| {
9635            *cache
9636                .rule_stop_reach
9637                .entry(state_number)
9638                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
9639        });
9640        self.rule_stop_reach_cache[state_number] = Some(reaches);
9641        reaches
9642    }
9643
9644    /// Returns the parser's empty `recovery_symbols` singleton so callers can
9645    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
9646    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
9647        Rc::clone(&self.empty_recovery_symbols)
9648    }
9649
9650    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
9651    /// recognizer can hash and compare keys by pointer.
9652    ///
9653    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
9654    /// come from this method or the empty singleton; otherwise two
9655    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
9656    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
9657    /// recognition coordinates.
9658    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
9659        if set.is_empty() {
9660            return Rc::clone(&self.empty_recovery_symbols);
9661        }
9662        let candidate = Rc::new(set);
9663        match self.recovery_symbols_intern.get(&candidate) {
9664            Some(existing) => Rc::clone(existing),
9665            None => {
9666                self.recovery_symbols_intern
9667                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
9668                candidate
9669            }
9670        }
9671    }
9672
9673    /// Returns the cached look-1 entry for a decision state, computing it on
9674    /// first use. Multi-alternative states are visited many times during
9675    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
9676    /// hash lookup per visit.
9677    fn cached_decision_lookahead(
9678        &mut self,
9679        atn: &Atn,
9680        state: AtnState<'_>,
9681        rule_stop_state: usize,
9682    ) -> Rc<DecisionLookahead> {
9683        // Hit the parser-instance cache first. Decision lookahead is purely
9684        // a function of the ATN/state, so on a warm cache we skip the
9685        // thread-local + RefCell + HashMap-entry dance through
9686        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
9687        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
9688        // hashmap-entry overhead in profiles.
9689        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
9690            return Rc::clone(cached);
9691        }
9692        let entry = with_shared_atn_caches(atn, |cache| {
9693            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
9694                return Rc::clone(cached);
9695            }
9696            let mut entry = DecisionLookahead {
9697                transitions: Vec::with_capacity(state.transitions().len()),
9698            };
9699            for transition in &state.transitions() {
9700                entry.transitions.push(transition_first_set(
9701                    atn,
9702                    transition,
9703                    rule_stop_state,
9704                    &mut cache.first_set,
9705                ));
9706            }
9707            let entry = Rc::new(entry);
9708            cache
9709                .decision_lookahead
9710                .insert(state.state_number(), Rc::clone(&entry));
9711            entry
9712        });
9713        self.decision_lookahead_cache
9714            .insert(state.state_number(), Rc::clone(&entry));
9715        entry
9716    }
9717
9718    fn cached_rule_first_set(
9719        &mut self,
9720        atn: &Atn,
9721        target: usize,
9722        child_stop: usize,
9723    ) -> Rc<FirstSet> {
9724        if self.rule_first_set_cache.len() <= target {
9725            self.rule_first_set_cache
9726                .resize_with(atn.states().len().max(target + 1), || None);
9727        }
9728        if let Some(cached) = self
9729            .rule_first_set_cache
9730            .get(target)
9731            .and_then(Option::as_ref)
9732        {
9733            return Rc::clone(cached);
9734        }
9735        let first = with_shared_first_set_cache(atn, |cache| {
9736            rule_first_set(atn, target, child_stop, cache)
9737        });
9738        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
9739        first
9740    }
9741
9742    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
9743        if self.empty_cycle_cache.len() <= state_number {
9744            self.empty_cycle_cache
9745                .resize_with(atn.states().len().max(state_number + 1), || None);
9746        }
9747        if let Some(cached) = self.empty_cycle_cache[state_number] {
9748            return cached;
9749        }
9750        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
9751        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
9752        self.empty_cycle_cache[state_number] = Some(result);
9753        result
9754    }
9755
9756    fn empty_path_reaches_state(
9757        &mut self,
9758        atn: &Atn,
9759        state_number: usize,
9760        target_state: usize,
9761        visited: &mut FxHashSet<usize>,
9762    ) -> bool {
9763        if !visited.insert(state_number) {
9764            return false;
9765        }
9766        let Some(state) = atn.state(state_number) else {
9767            return false;
9768        };
9769        for transition in &state.transitions() {
9770            let kind = transition.kind();
9771            let target = transition.target();
9772            match kind {
9773                ParserTransitionKind::Atom
9774                | ParserTransitionKind::Range
9775                | ParserTransitionKind::Set
9776                | ParserTransitionKind::NotSet
9777                | ParserTransitionKind::Wildcard => {}
9778                ParserTransitionKind::Rule => {
9779                    let rule_index = transition.arg0() as usize;
9780                    let follow_state = transition.arg1() as usize;
9781                    if target == target_state
9782                        || self.empty_path_reaches_state(atn, target, target_state, visited)
9783                    {
9784                        return true;
9785                    }
9786                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
9787                        continue;
9788                    };
9789                    if self.cached_rule_first_set(atn, target, child_stop).nullable
9790                        && (follow_state == target_state
9791                            || self.empty_path_reaches_state(
9792                                atn,
9793                                follow_state,
9794                                target_state,
9795                                visited,
9796                            ))
9797                    {
9798                        return true;
9799                    }
9800                }
9801                ParserTransitionKind::Epsilon
9802                | ParserTransitionKind::Predicate
9803                | ParserTransitionKind::Action
9804                | ParserTransitionKind::Precedence => {
9805                    if target == target_state
9806                        || self.empty_path_reaches_state(atn, target, target_state, visited)
9807                    {
9808                        return true;
9809                    }
9810                }
9811            }
9812        }
9813        false
9814    }
9815
9816    /// Decides whether a clean one-outcome entry is worth storing in the full
9817    /// outcome memo table for this parse.
9818    fn should_memoize_single_outcome(&mut self, key: &FastRecognizeKey) -> bool {
9819        match self.single_outcome_memo_mode {
9820            SingleOutcomeMemoMode::Promote => true,
9821            SingleOutcomeMemoMode::Sparse => false,
9822            SingleOutcomeMemoMode::Probe => {
9823                self.single_outcome_probe_samples += 1;
9824                if !self.single_outcome_probe_seen.insert(key.clone()) {
9825                    self.single_outcome_probe_repeats += 1;
9826                }
9827                if self.single_outcome_probe_repeats >= CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
9828                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Promote;
9829                    self.single_outcome_probe_seen.clear();
9830                    return true;
9831                }
9832                if self.single_outcome_probe_samples >= CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT {
9833                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Sparse;
9834                    self.single_outcome_probe_seen.clear();
9835                    return false;
9836                }
9837                true
9838            }
9839        }
9840    }
9841
9842    /// Borrows the visible token at an absolute token-stream index.
9843    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
9844        self.input.get(index)
9845    }
9846
9847    /// Returns the compact token ID at an absolute token-stream index.
9848    fn token_id_at(&self, index: usize) -> Option<TokenId> {
9849        self.input.get_id(index)
9850    }
9851
9852    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
9853        let token = self
9854            .token_id_at(index)
9855            .expect("recognized token index must exist in the token store");
9856        let node = if error {
9857            ArenaRecognizedNode::ErrorToken { token }
9858        } else {
9859            ArenaRecognizedNode::Token { token }
9860        };
9861        self.recognition_arena.push_node(node)
9862    }
9863
9864    fn arena_missing_token_node(
9865        &mut self,
9866        token_type: i32,
9867        at_index: usize,
9868        text: String,
9869    ) -> RecognizedNodeId {
9870        let extra = self
9871            .recognition_arena
9872            .push_extra(RecognitionExtra::MissingToken {
9873                token_type,
9874                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
9875                text,
9876            });
9877        self.recognition_arena
9878            .push_node(ArenaRecognizedNode::MissingToken { extra })
9879    }
9880
9881    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
9882        let ArenaRuleSpec {
9883            rule_index,
9884            invoking_state,
9885            alt_number,
9886            start_index,
9887            stop_index,
9888            return_values,
9889            children,
9890        } = spec;
9891        let return_values = (!return_values.is_empty()).then(|| {
9892            self.recognition_arena
9893                .push_extra(RecognitionExtra::ReturnValues(return_values))
9894        });
9895        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
9896            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
9897            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
9898            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
9899            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
9900            stop_index: stop_index
9901                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
9902            return_values,
9903            children,
9904        })
9905    }
9906
9907    fn arena_boundary_node(&mut self, rule_index: usize) -> RecognizedNodeId {
9908        self.recognition_arena
9909            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
9910                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
9911            })
9912    }
9913
9914    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
9915        *sequence = self.recognition_arena.prepend(*sequence, node);
9916    }
9917
9918    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
9919        self.last_recognition_arena_root = root;
9920        self.last_recognition_arena_diagnostics = diagnostics;
9921        #[cfg(feature = "perf-counters")]
9922        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
9923            let stats = self.recognition_arena_stats();
9924            #[allow(clippy::print_stderr)]
9925            {
9926                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
9927                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
9928                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
9929                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
9930                eprintln!("perf recognition_links_total={}", stats.total_links);
9931                eprintln!("perf recognition_links_live={}", stats.live_links);
9932                eprintln!("perf recognition_links_dead={}", stats.dead_links);
9933                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
9934                eprintln!("perf recognition_extras_total={}", stats.total_extras);
9935                eprintln!("perf recognition_extras_live={}", stats.live_extras);
9936                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
9937                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
9938            }
9939        }
9940    }
9941
9942    fn reset_recognition_arena(&mut self) {
9943        self.recognition_arena.reset();
9944        self.last_recognition_arena_root = NodeSeqId::EMPTY;
9945        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
9946    }
9947
9948    /// Normalizes the current token-stream cursor to the next parser-visible
9949    /// token before capturing a rule start boundary.
9950    fn current_visible_index(&mut self) -> usize {
9951        let index = self.input.index();
9952        self.input.seek(index);
9953        self.input.index()
9954    }
9955
9956    /// Reports whether a child rule reached EOF cleanly while also recording
9957    /// an EOF expectation from a longer path inside that child.
9958    fn child_expected_reaches_clean_eof(
9959        &mut self,
9960        children: &[RecognizeOutcome],
9961        expected: &ExpectedTokens,
9962    ) -> bool {
9963        let Some(index) = expected.index else {
9964            return false;
9965        };
9966        self.token_type_at(index) == TOKEN_EOF
9967            && children
9968                .iter()
9969                .any(|child| child.diagnostics.is_empty() && child.index == index)
9970    }
9971
9972    /// Finds the previous token visible to the parser before `index`.
9973    ///
9974    /// The token stream cursor skips hidden-channel tokens, so subtracting one
9975    /// from a visible-token index can point at whitespace. Parser intervals use
9976    /// this helper to stop at the previous visible token while preserving hidden
9977    /// text inside the rendered interval.
9978    fn previous_token_index(&self, index: usize) -> Option<usize> {
9979        self.input.previous_visible_token_index(index)
9980    }
9981
9982    /// Returns the token-stream index used as a rule stop boundary.
9983    ///
9984    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
9985    /// stop at `index` rather than at the previous visible token.
9986    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
9987        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
9988            Some(index)
9989        } else {
9990            self.previous_token_index(index)
9991        }
9992    }
9993
9994    /// Stop-token index for a rule's `@after` action, matching the boundary that
9995    /// `finish_rule` records on the rule context.
9996    ///
9997    /// A rule that matched EOF leaves the cursor parked on the EOF token
9998    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
9999    /// the current index rather than the previous visible token. Without this,
10000    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
10001    /// report the token before EOF (or `None` for empty input), diverging from
10002    /// the rule context that `finish_rule` builds.
10003    ///
10004    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
10005    /// rule ends right before EOF without matching it (the cursor is parked on
10006    /// EOF, but the rule did not consume it). Prefer
10007    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
10008    /// rule context already recorded with the real flag. Kept for callers without
10009    /// the rule tree in hand.
10010    #[must_use]
10011    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
10012        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
10013        self.rule_stop_token_index(current_index, consumed_eof)
10014    }
10015
10016    /// Stop-token index for a rule's `@after` action, taken from the stop token
10017    /// the rule context already recorded.
10018    ///
10019    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
10020    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
10021    /// the rule context — even when the rule ends immediately before EOF without
10022    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
10023    /// to the cursor-based inference only when the tree carries no rule stop.
10024    #[must_use]
10025    pub fn after_action_stop_index_for_tree(
10026        &mut self,
10027        tree: ParseTree,
10028        current_index: usize,
10029    ) -> Option<usize> {
10030        if let Some(stop) = self
10031            .node(tree)
10032            .as_rule()
10033            .and_then(crate::tree::RuleNodeView::stop_id)
10034        {
10035            return Some(stop.index());
10036        }
10037        self.after_action_stop_index(current_index)
10038    }
10039
10040    /// Start-token index for a rule's `@after` action, taken from the start token
10041    /// the rule context already recorded.
10042    ///
10043    /// `enter_rule` sets the rule context start to the first visible token (it
10044    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
10045    /// `$text` in an `@after` action aligned with the rule context — even when the
10046    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
10047    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
10048    /// the tree carries no rule start.
10049    #[must_use]
10050    pub fn after_action_start_index_for_tree(
10051        &self,
10052        tree: ParseTree,
10053        fallback_index: usize,
10054    ) -> usize {
10055        if let Some(start) = self
10056            .node(tree)
10057            .as_rule()
10058            .and_then(crate::tree::RuleNodeView::start_id)
10059        {
10060            return start.index();
10061        }
10062        fallback_index
10063    }
10064
10065    /// Returns the rule stop token for a selected parse path.
10066    ///
10067    /// EOF transitions do not advance the token-stream cursor, so an EOF match
10068    /// must use the current token rather than the previous visible token.
10069    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
10070        self.rule_stop_token_index(index, consumed_eof)
10071            .and_then(|token_index| self.token_id_at(token_index))
10072    }
10073
10074    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
10075    ///
10076    /// Generated Java reports the failed-predicate message at the current
10077    /// lookahead, then consumes until rule recovery can resume. The metadata
10078    /// runtime models the same visible tree shape by keeping skipped tokens as
10079    /// error nodes and returning from the active rule at EOF.
10080    fn predicate_failure_recovery(
10081        &mut self,
10082        request: PredicateFailureRecovery<'_>,
10083    ) -> RecognizeOutcome {
10084        let PredicateFailureRecovery {
10085            rule_index,
10086            index,
10087            message,
10088            member_values,
10089            return_values,
10090            rule_alt_number,
10091        } = request;
10092        let rule_name = self
10093            .rule_names()
10094            .get(rule_index)
10095            .map_or_else(|| rule_index.to_string(), Clone::clone);
10096        let diagnostic = diagnostic_for_token(
10097            self.token_at(index).as_ref(),
10098            format!("rule {rule_name} {message}"),
10099        );
10100        let mut reversed_nodes = NodeSeqId::EMPTY;
10101        let mut next_index = index;
10102        loop {
10103            let symbol = self.token_type_at(next_index);
10104            if symbol == TOKEN_EOF {
10105                break;
10106            }
10107            let error = self.arena_token_node(next_index, true);
10108            self.arena_prepend(&mut reversed_nodes, error);
10109            let after = self.consume_index(next_index, symbol);
10110            if after == next_index {
10111                break;
10112            }
10113            next_index = after;
10114        }
10115        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
10116        let diagnostics = self
10117            .recognition_arena
10118            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10119        RecognizeOutcome {
10120            index: next_index,
10121            consumed_eof: false,
10122            alt_number: rule_alt_number,
10123            member_values,
10124            return_values,
10125            diagnostics,
10126            decisions: Vec::new(),
10127            actions: Vec::new(),
10128            nodes,
10129        }
10130    }
10131
10132    /// Evaluates a user hook for a predicate coordinate that has no generated
10133    /// runtime table entry.
10134    fn parser_semantic_hook_result(
10135        &mut self,
10136        request: ParserSemanticHookRequest<'_>,
10137    ) -> Option<bool> {
10138        let ParserSemanticHookRequest {
10139            index,
10140            rule_index,
10141            pred_index,
10142            context,
10143            local_int_arg,
10144            member_values,
10145        } = request;
10146        let rule_name = self.rule_names().get(rule_index).cloned();
10147        self.input.seek(index);
10148        let input = &mut self.input;
10149        let semantic_hooks = &mut self.semantic_hooks;
10150        let mut ctx = ParserSemCtx {
10151            input,
10152            tree_storage: &self.tree,
10153            rule_index,
10154            coordinate_index: pred_index,
10155            rule_name,
10156            context,
10157            tree: None,
10158            local_int_arg,
10159            member_values,
10160            action: None,
10161        };
10162        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
10163    }
10164
10165    /// Re-inserts unknown-predicate coordinates recorded before a nested
10166    /// interpreted recognition, preserving order and skipping any the nested
10167    /// call already recorded, so a generated parent's fail-loud coordinates
10168    /// survive descending into an interpreted child.
10169    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
10170        if prior.is_empty() {
10171            return;
10172        }
10173        let mut merged = prior;
10174        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
10175            if !merged.contains(&coordinate) {
10176                merged.push(coordinate);
10177            }
10178        }
10179        self.unknown_predicate_hits = merged;
10180    }
10181
10182    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
10183    /// that has no entry in the generated predicate table.
10184    ///
10185    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
10186    /// the guarded path is abandoned; the parse entry surfaces the recorded
10187    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
10188    /// because a parse that consulted an unknown predicate is unreliable no
10189    /// matter which paths were ultimately selected.
10190    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
10191        apply_unknown_predicate_policy(
10192            self.unknown_predicate_policy,
10193            rule_index,
10194            pred_index,
10195            &mut self.unknown_predicate_hits,
10196        )
10197    }
10198
10199    /// Builds the fail-loud error for unknown predicate coordinates recorded
10200    /// by the current parse, if any.
10201    fn unknown_semantic_error(&self) -> Option<AntlrError> {
10202        use std::fmt::Write as _;
10203        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
10204            return None;
10205        }
10206        let mut message = String::new();
10207        for (rule_index, pred_index) in &self.unknown_predicate_hits {
10208            if !message.is_empty() {
10209                message.push_str("; ");
10210            }
10211            let _ = match self.rule_names().get(*rule_index) {
10212                Some(rule_name) => write!(
10213                    message,
10214                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
10215                ),
10216                None => write!(
10217                    message,
10218                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
10219                ),
10220            };
10221        }
10222        for (rule_index, source_state) in &self.unhandled_action_hits {
10223            if !message.is_empty() {
10224                message.push_str("; ");
10225            }
10226            let _ = match self.rule_names().get(*rule_index) {
10227                Some(rule_name) => write!(
10228                    message,
10229                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
10230                ),
10231                None => write!(
10232                    message,
10233                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
10234                ),
10235            };
10236        }
10237        Some(AntlrError::Unsupported(message))
10238    }
10239
10240    /// Evaluates one lowered predicate expression at the requested input
10241    /// position.
10242    ///
10243    /// This sits in the prediction hot loop, so the context borrows the
10244    /// speculative member state read-only and the rule name by reference —
10245    /// no per-evaluation allocation. Only the hook escape path materializes
10246    /// owned copies, and only when a hook is actually consulted.
10247    fn parser_semir_predicate_matches(
10248        &mut self,
10249        semantics: &ParserSemantics,
10250        predicate: &ParserSemanticPredicate,
10251        request: ParserSemanticHookRequest<'_>,
10252    ) -> bool {
10253        self.input.seek(request.index);
10254        let rule_name = self
10255            .data
10256            .rule_names()
10257            .get(request.rule_index)
10258            .map(String::as_str);
10259        let unknown_predicate_policy = self.unknown_predicate_policy;
10260        let mut ctx = ParserSemIrCtx {
10261            input: &mut self.input,
10262            tree_storage: &self.tree,
10263            semantic_hooks: &mut self.semantic_hooks,
10264            rule_index: request.rule_index,
10265            coordinate_index: request.pred_index,
10266            rule_name,
10267            context: request.context,
10268            local_int_arg: request.local_int_arg,
10269            member_values: request.member_values,
10270            invoked_predicates: &mut self.invoked_predicates,
10271            unknown_predicate_policy,
10272            unknown_predicate_hits: &mut self.unknown_predicate_hits,
10273        };
10274        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
10275    }
10276
10277    fn fast_parser_predicate_matches(
10278        &mut self,
10279        context: Option<FastPredicateContext<'_>>,
10280        transition: ParserTransition<'_>,
10281        index: usize,
10282    ) -> bool {
10283        let Some(context) = context else {
10284            return true;
10285        };
10286        let rule_index = transition.arg0() as usize;
10287        let pred_index = transition.arg1() as usize;
10288        let key = (index, rule_index, pred_index);
10289        if let Some(result) = self.fast_predicate_cache.get(&key) {
10290            return *result;
10291        }
10292        let result = self.parser_predicate_matches(PredicateEval {
10293            index,
10294            rule_index,
10295            pred_index,
10296            predicates: context.predicates,
10297            semantics: context.semantics,
10298            context: None,
10299            local_int_arg: None,
10300            member_values: context.member_values,
10301        });
10302        self.fast_predicate_cache.insert(key, result);
10303        result
10304    }
10305
10306    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
10307        let PredicateEval {
10308            index,
10309            rule_index,
10310            pred_index,
10311            predicates,
10312            semantics,
10313            context,
10314            local_int_arg,
10315            member_values,
10316        } = eval;
10317        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
10318            semantics
10319                .predicates
10320                .iter()
10321                .find(|predicate| {
10322                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
10323                })
10324                .map(|predicate| (semantics, predicate))
10325        }) {
10326            return self.parser_semir_predicate_matches(
10327                semantics,
10328                predicate,
10329                ParserSemanticHookRequest {
10330                    index,
10331                    rule_index,
10332                    pred_index,
10333                    context,
10334                    local_int_arg,
10335                    member_values,
10336                },
10337            );
10338        }
10339        let Some((_, _, predicate)) = predicates
10340            .iter()
10341            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
10342        else {
10343            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
10344                index,
10345                rule_index,
10346                pred_index,
10347                context,
10348                local_int_arg,
10349                member_values,
10350            }) {
10351                return result;
10352            }
10353            return self.unknown_predicate_result(rule_index, pred_index);
10354        };
10355        self.input.seek(index);
10356        match predicate {
10357            ParserPredicate::True => true,
10358            ParserPredicate::False => false,
10359            ParserPredicate::FalseWithMessage { .. } => false,
10360            ParserPredicate::Invoke { value } => {
10361                let key = (rule_index, pred_index);
10362                if !self.invoked_predicates.contains(&key) {
10363                    self.invoked_predicates.push(key);
10364                    use std::io::Write as _;
10365                    let mut stdout = std::io::stdout().lock();
10366                    let _ = writeln!(stdout, "eval={value}");
10367                }
10368                *value
10369            }
10370            ParserPredicate::LookaheadTextEquals { offset, text } => self
10371                .input
10372                .lt(*offset)
10373                .is_some_and(|token| Token::text(&token) == Some(*text)),
10374            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
10375                self.la(*offset) != *token_type
10376            }
10377            ParserPredicate::TokenPairAdjacent => {
10378                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
10379                    return false;
10380                };
10381                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
10382                    return false;
10383                };
10384                first + 1 == second
10385            }
10386            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
10387                .and_then(|context| {
10388                    context
10389                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
10390                        .next()
10391                        .map(crate::tree::RuleNodeView::text)
10392                })
10393                .is_none_or(|actual| actual != *text),
10394            ParserPredicate::LocalIntEquals { value } => {
10395                local_int_arg.is_none_or(|(_, actual)| actual == *value)
10396            }
10397            ParserPredicate::LocalIntLessOrEqual { value } => {
10398                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
10399            }
10400            ParserPredicate::MemberModuloEquals {
10401                member,
10402                modulus,
10403                value,
10404                equals,
10405            } => {
10406                if *modulus == 0 {
10407                    return false;
10408                }
10409                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
10410                (actual == *value) == *equals
10411            }
10412            ParserPredicate::MemberEquals {
10413                member,
10414                value,
10415                equals,
10416            } => {
10417                let actual = member_values.get(member).copied().unwrap_or_default();
10418                (actual == *value) == *equals
10419            }
10420        }
10421    }
10422
10423    /// Returns a generated fail-option message for a predicate coordinate.
10424    fn parser_predicate_failure_message(
10425        &self,
10426        rule_index: usize,
10427        pred_index: usize,
10428        predicates: &[(usize, usize, ParserPredicate)],
10429    ) -> Option<&'static str> {
10430        predicates
10431            .iter()
10432            .find_map(|(rule, pred, predicate)| match predicate {
10433                ParserPredicate::FalseWithMessage { message }
10434                    if *rule == rule_index && *pred == pred_index =>
10435                {
10436                    Some(*message)
10437                }
10438                _ => None,
10439            })
10440    }
10441
10442    /// Returns a generated fail-option message for a `SemIR` predicate
10443    /// coordinate.
10444    pub fn parser_semantic_ir_predicate_failure_message(
10445        &self,
10446        rule_index: usize,
10447        pred_index: usize,
10448        semantics: &ParserSemantics,
10449    ) -> Option<&'static str> {
10450        semantics
10451            .predicates
10452            .iter()
10453            .find(|predicate| {
10454                predicate.rule_index == rule_index && predicate.pred_index == pred_index
10455            })
10456            .and_then(|predicate| predicate.failure_message)
10457    }
10458
10459    /// Returns the token-stream index after consuming `symbol` at `index`.
10460    ///
10461    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
10462    /// index stable and rely on `consumed_eof` to record that EOF was matched.
10463    /// The parser's stream cursor is left untouched: speculative recognition
10464    /// reads ahead by absolute index, so paying for `seek` on every visited
10465    /// state would dominate the hot path. Real consumption is committed by
10466    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
10467    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
10468        if symbol == TOKEN_EOF {
10469            return index;
10470        }
10471        self.input.next_visible_after(index)
10472    }
10473
10474    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
10475    /// decision that failed after consuming a shared prefix.
10476    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
10477        let text = display_input_text(&self.input.text(start_index, error_index));
10478        diagnostic_for_token(
10479            self.token_at(error_index).as_ref(),
10480            format!("no viable alternative at input '{text}'"),
10481        )
10482    }
10483
10484    /// Selects the diagnostic for a failed consuming transition after all
10485    /// recovery repairs have been ruled out.
10486    fn recovery_failure_diagnostic(
10487        &self,
10488        index: usize,
10489        decision_start_index: Option<usize>,
10490        expected_symbols: &BTreeSet<i32>,
10491    ) -> ParserDiagnostic {
10492        if expected_symbols.len() > 1 {
10493            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
10494                return self.no_viable_alternative(decision_start, index);
10495            }
10496        }
10497        diagnostic_for_token(
10498            self.token_at(index).as_ref(),
10499            format!(
10500                "mismatched input {} expecting {}",
10501                self.token_at(index)
10502                    .as_ref()
10503                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10504                self.expected_symbols_display(expected_symbols)
10505            ),
10506        )
10507    }
10508
10509    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
10510    /// instead of inserting missing tokens in the caller.
10511    fn eof_rule_recovery_diagnostic(
10512        &self,
10513        index: usize,
10514        expected_symbols: &BTreeSet<i32>,
10515        expected: &ExpectedTokens,
10516    ) -> ParserDiagnostic {
10517        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
10518            &expected.symbols
10519        } else {
10520            expected_symbols
10521        };
10522        diagnostic_for_token(
10523            self.token_at(index).as_ref(),
10524            format!(
10525                "mismatched input {} expecting {}",
10526                self.token_at(index)
10527                    .as_ref()
10528                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10529                self.expected_symbols_display(symbols)
10530            ),
10531        )
10532    }
10533
10534    /// Returns token text for a buffered token interval used by generated
10535    /// `$text` actions.
10536    ///
10537    /// ANTLR treats EOF as a range boundary rather than printable input text,
10538    /// even when an action interval explicitly stops at the EOF token.
10539    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
10540        let Some(stop) = stop else {
10541            return String::new();
10542        };
10543        let stop = if self
10544            .token_at(stop)
10545            .is_some_and(|token| token.token_type() == TOKEN_EOF)
10546        {
10547            let Some(previous) = self.previous_token_index(stop) else {
10548                return String::new();
10549            };
10550            previous
10551        } else {
10552            stop
10553        };
10554        self.input.text(start, stop)
10555    }
10556
10557    /// Resets per-parse prediction diagnostics while keeping the parser-level
10558    /// reporting flag configured by generated harness code.
10559    fn clear_prediction_diagnostics(&mut self) {
10560        self.prediction_diagnostics.clear();
10561        self.reported_prediction_diagnostics.clear();
10562    }
10563
10564    /// Drops every per-parse cache that depends on ATN identity or pins
10565    /// recovery-symbol allocations.
10566    ///
10567    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
10568    /// same parser instance can legally be driven against different grammars
10569    /// in sequence. The four caches reset here are keyed by raw ATN
10570    /// coordinates (state numbers, rule indexes) and would silently hand back
10571    /// entries from a previous ATN if reused — pruning lookahead against the
10572    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
10573    /// for the rest of the process. Clearing them on every parse entry keeps
10574    /// the perf wins (caches still amortize within one parse) without making
10575    /// long-lived parsers leak memory or surface stale ATN data:
10576    ///
10577    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
10578    ///   functions of the ATN's state graph.
10579    /// * `state_expected_cache`, `state_expected_token_cache`,
10580    ///   `rule_stop_reach_cache`, and
10581    ///   `recovery_symbols_intern` together form
10582    ///   the identity invariant that lets `FastRecognizeKey` hash
10583    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
10584    ///   so a stale interned `Rc` cannot outlive its map entry.
10585    fn reset_per_parse_caches(&mut self) {
10586        self.rule_first_set_cache.clear();
10587        self.decision_lookahead_cache.clear();
10588        self.ll1_decision_cache.clear();
10589        self.fast_predicate_cache.clear();
10590        self.empty_cycle_cache.clear();
10591        self.rule_stop_reach_cache.clear();
10592        self.single_outcome_memo_mode = SingleOutcomeMemoMode::Probe;
10593        self.single_outcome_probe_seen.clear();
10594        self.single_outcome_probe_samples = 0;
10595        self.single_outcome_probe_repeats = 0;
10596        self.recovery_symbols_intern.clear();
10597        self.state_expected_cache.clear();
10598        self.state_expected_token_cache.clear();
10599    }
10600
10601    /// Buffers ANTLR-style diagnostic-listener messages for decision states
10602    /// where multiple clean alternatives survive full-context recognition.
10603    fn record_prediction_diagnostics(
10604        &mut self,
10605        atn: &Atn,
10606        state: AtnState<'_>,
10607        start_index: usize,
10608        outcomes: &[RecognizeOutcome],
10609    ) {
10610        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
10611            return;
10612        }
10613        let Some(decision) = atn
10614            .decision_to_state()
10615            .iter()
10616            .position(|state_number| state_number == state.state_number())
10617        else {
10618            return;
10619        };
10620        let Some(rule_index) = state.rule_index() else {
10621            return;
10622        };
10623        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
10624        for outcome in outcomes
10625            .iter()
10626            .filter(|outcome| outcome.diagnostics.is_empty())
10627        {
10628            let Some(alt) = outcome.decisions.first() else {
10629                continue;
10630            };
10631            alts_by_end
10632                .entry(outcome.index)
10633                .or_default()
10634                .insert(alt + 1);
10635        }
10636        let Some((&end_index, ambig_alts)) = alts_by_end
10637            .iter()
10638            .filter(|(_, alts)| alts.len() > 1)
10639            .max_by_key(|(end, _)| *end)
10640        else {
10641            return;
10642        };
10643        let rule_name = self
10644            .rule_names()
10645            .get(rule_index)
10646            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
10647        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
10648        let input = display_input_text(&self.input.text(start_index, stop_index));
10649        let alts = ambig_alts
10650            .iter()
10651            .map(usize::to_string)
10652            .collect::<Vec<_>>()
10653            .join(", ");
10654        let key = (decision, start_index, format!("{alts}:{input}"));
10655        if !self.reported_prediction_diagnostics.insert(key) {
10656            return;
10657        }
10658        let start_diagnostic = diagnostic_for_token(
10659            self.token_at(start_index),
10660            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
10661        );
10662        let stop_diagnostic = diagnostic_for_token(
10663            self.token_at(stop_index),
10664            format!(
10665                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
10666            ),
10667        );
10668        self.prediction_diagnostics.push(start_diagnostic);
10669        self.prediction_diagnostics.push(stop_diagnostic);
10670    }
10671
10672    /// Formats the tokens expected from an ATN state using ANTLR display names.
10673    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
10674        expected_symbols_display(
10675            &state_expected_symbols(atn, state_number),
10676            self.vocabulary(),
10677        )
10678    }
10679
10680    /// Expected-token set at the parser's current ATN state — ANTLR's
10681    /// `getExpectedTokens()`. Generated recognizers expose this as
10682    /// `self.expected_tokens()` for embedded test actions
10683    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
10684    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
10685        let state = usize::try_from(self.data().state()).unwrap_or(0);
10686        ExpectedTokenSet {
10687            symbols: state_expected_symbols(atn, state),
10688        }
10689    }
10690
10691    /// Enables the bail error strategy: the first syntax error aborts the
10692    /// parse instead of recovering.
10693    pub const fn set_bail_on_error(&mut self, bail: bool) {
10694        self.bail_on_error = bail;
10695    }
10696
10697    /// Whether the bail error strategy is active.
10698    #[must_use]
10699    pub const fn bail_on_error(&self) -> bool {
10700        self.bail_on_error
10701    }
10702
10703    /// Names of the rules on the live invocation stack, current rule first —
10704    /// ANTLR's `getRuleInvocationStack()`.
10705    pub fn rule_invocation_stack(&self) -> Vec<String> {
10706        self.rule_context_stack
10707            .iter()
10708            .rev()
10709            .map(|frame| {
10710                self.data()
10711                    .rule_names()
10712                    .get(frame.rule_index)
10713                    .cloned()
10714                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
10715            })
10716            .collect()
10717    }
10718
10719    /// Invoking-state chain for the active rule context, current rule first.
10720    ///
10721    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
10722    pub fn active_invocation_states(&self) -> Vec<isize> {
10723        self.rule_context_stack
10724            .iter()
10725            .skip(1)
10726            .rev()
10727            .map(|frame| frame.invoking_state)
10728            .collect()
10729    }
10730
10731    /// Formats a buffered token in ANTLR's diagnostic token display form.
10732    pub fn token_display_at(&self, index: usize) -> Option<String> {
10733        self.token_at(index).map(|token| format!("{token}"))
10734    }
10735}
10736
10737impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
10738where
10739    S: TokenSource,
10740    H: SemanticHooks,
10741{
10742    fn parse_rule(
10743        &mut self,
10744        rule_index: usize,
10745        invoking_state: isize,
10746        precedence: i32,
10747    ) -> DirectAdaptiveParseResult<ParseTree> {
10748        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
10749            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
10750        )?;
10751        let stop_state = self
10752            .atn
10753            .rule_to_stop_state()
10754            .get(rule_index)
10755            .filter(|state| *state != usize::MAX)
10756            .ok_or(DirectAdaptiveParseControl::Fallback(
10757                DirectAdaptiveFallback::MissingAtn,
10758            ))?;
10759        let start_index = self.parser.current_visible_index();
10760        let mut context = ParserRuleContext::new(rule_index, invoking_state);
10761        if let Some(token) = self.parser.token_id_at(start_index) {
10762            self.parser.set_context_start(&mut context, token);
10763        }
10764        let mut state_number = start_state;
10765        let mut consumed_eof = false;
10766        while state_number != stop_state {
10767            self.step()?;
10768            let (transition, boundary) = self.next_transition(state_number, precedence)?;
10769            if boundary.is_some() {
10770                return Err(DirectAdaptiveParseControl::Fallback(
10771                    DirectAdaptiveFallback::LeftRecursiveBoundary,
10772                ));
10773            }
10774            match transition.data() {
10775                Transition::Epsilon { target } => {
10776                    state_number = target;
10777                }
10778                Transition::Precedence {
10779                    target,
10780                    precedence: transition_precedence,
10781                } => {
10782                    if transition_precedence < precedence {
10783                        return Err(DirectAdaptiveParseControl::Fallback(
10784                            DirectAdaptiveFallback::Precedence,
10785                        ));
10786                    }
10787                    state_number = target;
10788                }
10789                Transition::Rule {
10790                    rule_index,
10791                    follow_state,
10792                    precedence: rule_precedence,
10793                    ..
10794                } => {
10795                    let child = self.parse_rule(
10796                        rule_index,
10797                        invoking_state_number(state_number),
10798                        rule_precedence,
10799                    )?;
10800                    if self.parser.build_parse_trees {
10801                        self.parser.tree.add_child(&mut context, child);
10802                    }
10803                    state_number = follow_state;
10804                }
10805                Transition::Atom { .. }
10806                | Transition::Range { .. }
10807                | Transition::Set { .. }
10808                | Transition::NotSet { .. }
10809                | Transition::Wildcard { .. } => {
10810                    let (matched_eof, child) = self.consume_transition(transition)?;
10811                    consumed_eof |= matched_eof;
10812                    if let Some(child) = child {
10813                        self.parser.tree.add_child(&mut context, child);
10814                    }
10815                    state_number = transition.target();
10816                }
10817                Transition::Predicate { .. } => {
10818                    return Err(DirectAdaptiveParseControl::Fallback(
10819                        DirectAdaptiveFallback::Predicate,
10820                    ));
10821                }
10822                Transition::Action { .. } => {
10823                    return Err(DirectAdaptiveParseControl::Fallback(
10824                        DirectAdaptiveFallback::Action,
10825                    ));
10826                }
10827            }
10828        }
10829
10830        let stop_index = self
10831            .parser
10832            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
10833        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
10834            self.parser.set_context_stop(&mut context, token);
10835        }
10836        Ok(self.parser.rule_node(context))
10837    }
10838
10839    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
10840        self.steps += 1;
10841        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
10842            return Err(DirectAdaptiveParseControl::Fallback(
10843                DirectAdaptiveFallback::StepLimit,
10844            ));
10845        }
10846        Ok(())
10847    }
10848
10849    fn next_transition(
10850        &mut self,
10851        state_number: usize,
10852        precedence: i32,
10853    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
10854        let state = self
10855            .atn
10856            .state(state_number)
10857            .ok_or(DirectAdaptiveParseControl::Fallback(
10858                DirectAdaptiveFallback::MissingAtn,
10859            ))?;
10860        if state.is_rule_stop() {
10861            return Err(DirectAdaptiveParseControl::Fallback(
10862                DirectAdaptiveFallback::RuleStop,
10863            ));
10864        }
10865        let transition_index =
10866            self.transition_index(state_number, state.transitions().len(), precedence)?;
10867        let transition = state.transitions().get(transition_index).ok_or(
10868            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
10869        )?;
10870        let boundary = match &transition.data() {
10871            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
10872                left_recursive_boundary(self.atn, state, *target)
10873            }
10874            _ => None,
10875        };
10876        Ok((transition, boundary))
10877    }
10878
10879    fn transition_index(
10880        &mut self,
10881        state_number: usize,
10882        transition_count: usize,
10883        precedence: i32,
10884    ) -> DirectAdaptiveParseResult<usize> {
10885        match transition_count {
10886            0 => Err(DirectAdaptiveParseControl::Fallback(
10887                DirectAdaptiveFallback::NoTransition,
10888            )),
10889            1 => Ok(0),
10890            _ => {
10891                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
10892                    return Ok(alt);
10893                }
10894                let decision = self
10895                    .decision_by_state
10896                    .get(state_number)
10897                    .and_then(|decision| *decision)
10898                    .ok_or(DirectAdaptiveParseControl::Fallback(
10899                        DirectAdaptiveFallback::UnknownDecision,
10900                    ))?;
10901                let prediction = self
10902                    .simulator
10903                    .adaptive_predict_stream_info_with_precedence(
10904                        decision,
10905                        direct_precedence(precedence),
10906                        &mut self.parser.input,
10907                    )
10908                    .map_err(|_| {
10909                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
10910                    })?;
10911                if prediction.has_semantic_context {
10912                    return Err(DirectAdaptiveParseControl::Fallback(
10913                        DirectAdaptiveFallback::SemanticContext,
10914                    ));
10915                }
10916                prediction
10917                    .alt
10918                    .checked_sub(1)
10919                    .filter(|index| *index < transition_count)
10920                    .ok_or(DirectAdaptiveParseControl::Fallback(
10921                        DirectAdaptiveFallback::InvalidAlt,
10922                    ))
10923            }
10924        }
10925    }
10926
10927    fn ll1_transition_index(
10928        &mut self,
10929        state_number: usize,
10930        transition_count: usize,
10931    ) -> DirectAdaptiveParseResult<Option<usize>> {
10932        let state = self
10933            .atn
10934            .state(state_number)
10935            .ok_or(DirectAdaptiveParseControl::Fallback(
10936                DirectAdaptiveFallback::MissingAtn,
10937            ))?;
10938        if state.precedence_rule_decision() {
10939            return Ok(None);
10940        }
10941        let Some(rule_stop) = state
10942            .rule_index()
10943            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
10944        else {
10945            return Ok(None);
10946        };
10947        let symbol = self.parser.input.la_token(1);
10948        let entry = self
10949            .parser
10950            .cached_decision_lookahead(self.atn, state, rule_stop);
10951        Ok(
10952            ll1_greedy_alt(&entry, symbol, state.non_greedy())
10953                .filter(|alt| *alt < transition_count),
10954        )
10955    }
10956
10957    fn consume_transition(
10958        &mut self,
10959        transition: ParserTransition<'_>,
10960    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
10961        let symbol = self.parser.input.la_token(1);
10962        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
10963            return Err(DirectAdaptiveParseControl::Fallback(
10964                DirectAdaptiveFallback::TokenMismatch,
10965            ));
10966        }
10967        let token = self
10968            .parser
10969            .input
10970            .lt_id(1)
10971            .ok_or(DirectAdaptiveParseControl::Fallback(
10972                DirectAdaptiveFallback::TokenMismatch,
10973            ))?;
10974        let matched_eof = symbol == TOKEN_EOF;
10975        if !matched_eof {
10976            self.parser.consume();
10977        }
10978        let child = self
10979            .parser
10980            .build_parse_trees
10981            .then(|| self.parser.terminal_tree(token));
10982        Ok((matched_eof, child))
10983    }
10984}
10985
10986/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
10987/// transformed left-recursive rule.
10988fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
10989    if !state.precedence_rule_decision() {
10990        return None;
10991    }
10992    let target_state = atn.state(target)?;
10993    if target_state.kind() == AtnStateKind::LoopEnd {
10994        return None;
10995    }
10996    state.rule_index()
10997}
10998
10999/// Selects the first outer alternative observed for a rule path.
11000///
11001/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
11002/// outer decision. The metadata recognizer only needs this when a generated
11003/// grammar opts into that target template; otherwise the value remains `0` and
11004/// parse-tree rendering is unchanged.
11005fn next_alt_number(
11006    state: AtnState<'_>,
11007    transition_count: usize,
11008    transition_index: usize,
11009    current_alt_number: usize,
11010    track_alt_numbers: bool,
11011) -> usize {
11012    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
11013        return current_alt_number;
11014    }
11015    if matches!(
11016        state.kind(),
11017        AtnStateKind::Basic
11018            | AtnStateKind::BlockStart
11019            | AtnStateKind::PlusBlockStart
11020            | AtnStateKind::StarBlockStart
11021            | AtnStateKind::StarLoopEntry
11022    ) && !state.precedence_rule_decision()
11023    {
11024        return transition_index + 1;
11025    }
11026    current_alt_number
11027}
11028
11029/// Converts an ATN state number into the signed invoking-state slot used by
11030/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
11031fn invoking_state_number(state_number: usize) -> isize {
11032    isize::try_from(state_number).unwrap_or(isize::MAX)
11033}
11034
11035const fn packed_i32(value: u32) -> i32 {
11036    i32::from_le_bytes(value.to_le_bytes())
11037}
11038
11039fn direct_precedence(precedence: i32) -> usize {
11040    usize::try_from(precedence.max(0)).unwrap_or_default()
11041}
11042
11043fn token_input_display(token: &impl Token) -> String {
11044    format!("'{}'", token.text().unwrap_or("<EOF>"))
11045}
11046
11047fn display_input_text(text: &str) -> String {
11048    let mut out = String::new();
11049    for ch in text.chars() {
11050        match ch {
11051            '\n' => out.push_str("\\n"),
11052            '\r' => out.push_str("\\r"),
11053            '\t' => out.push_str("\\t"),
11054            other => out.push(other),
11055        }
11056    }
11057    out
11058}
11059
11060fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
11061    let (line, column) = token.map_or((0, 0), |token| (token.line(), token.column()));
11062    ParserDiagnostic {
11063        line,
11064        column,
11065        message,
11066    }
11067}
11068
11069/// Emits parser diagnostics for the selected recovered parse path.
11070#[allow(clippy::print_stderr)]
11071fn report_parser_diagnostics<'a>(diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>) {
11072    for diagnostic in diagnostics {
11073        eprintln!(
11074            "line {}:{} {}",
11075            diagnostic.line, diagnostic.column, diagnostic.message
11076        );
11077    }
11078}
11079
11080/// Emits generated parser diagnostics and lexer diagnostics in the same
11081/// source-position order as ANTLR's lazy token stream reports them.
11082#[allow(clippy::print_stderr)]
11083fn report_generated_diagnostics(
11084    parser_diagnostics: &[ParserDiagnostic],
11085    token_errors: &[TokenSourceError],
11086) {
11087    // Parser diagnostics keep their event order: Java's console and
11088    // DiagnosticErrorListener print reports as prediction produces them, so
11089    // `reportAttemptingFullContext` precedes `reportContextSensitivity` even
11090    // though the latter's position is earlier. Buffered token-source errors
11091    // interleave by source position — ANTLR's lazy token stream surfaces a
11092    // lexer error when the parser first fetches that token — and win ties.
11093    let mut token_iter = token_errors.iter().peekable();
11094    for diagnostic in parser_diagnostics {
11095        while let Some(error) = token_iter.peek() {
11096            if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
11097                eprintln!("line {}:{} {}", error.line, error.column, error.message);
11098                token_iter.next();
11099            } else {
11100                break;
11101            }
11102        }
11103        eprintln!(
11104            "line {}:{} {}",
11105            diagnostic.line, diagnostic.column, diagnostic.message
11106        );
11107    }
11108    for error in token_iter {
11109        eprintln!("line {}:{} {}", error.line, error.column, error.message);
11110    }
11111}
11112
11113/// Emits buffered token-source diagnostics after parser diagnostics that were
11114/// discovered while speculatively reading the same token stream.
11115#[allow(clippy::print_stderr)]
11116fn report_token_source_errors(errors: &[TokenSourceError]) {
11117    for error in errors {
11118        eprintln!("line {}:{} {}", error.line, error.column, error.message);
11119    }
11120}
11121
11122fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
11123    let items = symbols
11124        .iter()
11125        .map(|symbol| expected_symbol_display(*symbol, vocabulary))
11126        .collect::<Vec<_>>();
11127    if let [single] = items.as_slice() {
11128        return single.clone();
11129    }
11130    format!("{{{}}}", items.join(", "))
11131}
11132
11133fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
11134    if symbol == TOKEN_EOF {
11135        return "<EOF>".to_owned();
11136    }
11137    vocabulary.display_name(symbol)
11138}
11139
11140fn caller_follow_token_info_for_stream<S: TokenSource>(
11141    input: &mut CommonTokenStream<S>,
11142    index: usize,
11143) -> (i32, bool, bool) {
11144    // Generated callers own statement separators; leave them available when
11145    // an interpreted child rule can either stop before or consume one.
11146    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
11147        input.fill();
11148    }
11149    let token_type = input.token_type_at_index(index);
11150    let visible_channel = input.channel();
11151    let token = input.get(index);
11152    let is_boundary = token
11153        .as_ref()
11154        .and_then(Token::text)
11155        .is_some_and(is_caller_follow_boundary_text);
11156    let is_boundary_gap = token.as_ref().is_some_and(|token| {
11157        token.channel() != visible_channel || is_caller_follow_boundary_gap_text(token.text())
11158    });
11159    (token_type, is_boundary, is_boundary_gap)
11160}
11161
11162fn is_caller_follow_boundary_text(text: &str) -> bool {
11163    text.chars().any(|ch| ch == ';' || ch == '\n')
11164        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11165}
11166
11167fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
11168    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11169}
11170
11171/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
11172/// Inline insertion in those precedence loops can synthesize a missing operand
11173/// before an operator and then block the legitimate loop-exit path.
11174fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
11175    let Some(rule_index) = state.rule_index() else {
11176        return false;
11177    };
11178    atn.rule_to_start_state()
11179        .get(rule_index)
11180        .and_then(|state_number| atn.state(state_number))
11181        .is_some_and(AtnState::left_recursive_rule)
11182}
11183
11184/// Picks the better of two `parse_atn_rule` passes (with and without the
11185/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
11186/// recovered one; among recovered outcomes the second pass is preferred
11187/// because the no-prefilter walk reaches ANTLR-style recovery inside child
11188/// rules. If both passes failed, the second pass's expected-token snapshot
11189/// is returned so the caller renders the same diagnostic ANTLR would.
11190fn select_better_top_outcome(
11191    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11192    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11193    arena: &RecognitionArena,
11194) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
11195    match (first, second) {
11196        (Ok(first), Ok(second)) => {
11197            if arena.diagnostics(first.0.diagnostics).next().is_none() {
11198                Ok(first)
11199            } else {
11200                Ok(second)
11201            }
11202        }
11203        (Ok(first), Err(_)) => Ok(first),
11204        (Err(_), Ok(second)) => Ok(second),
11205        (Err(_), Err(second_expected)) => Err(second_expected),
11206    }
11207}
11208
11209/// Chooses the outermost parse result that consumed the most input.
11210///
11211/// The recognizer intentionally keeps shorter endpoints available while walking
11212/// nested rule transitions so callers can satisfy following tokens such as
11213/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
11214fn select_best_fast_outcome(
11215    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
11216    prediction_mode: PredictionMode,
11217    caller_follow: Option<&TokenBitSet>,
11218    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
11219    arena: &RecognitionArena,
11220) -> Option<FastRecognizeOutcome> {
11221    let mut best = None;
11222    let mut best_caller_follow = None;
11223    for outcome in outcomes {
11224        if matches!(
11225            prediction_mode,
11226            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11227        ) && outcome.diagnostics.is_empty()
11228            && let Some(follow) = caller_follow
11229        {
11230            let (token_type, is_boundary, _) = token_info_at(outcome.index);
11231            if is_boundary && follow.contains(token_type) {
11232                let replace =
11233                    best_caller_follow
11234                        .as_ref()
11235                        .is_none_or(|existing: &FastRecognizeOutcome| {
11236                            (outcome.index, outcome.consumed_eof)
11237                                < (existing.index, existing.consumed_eof)
11238                        });
11239                if replace {
11240                    best_caller_follow = Some(outcome);
11241                }
11242            }
11243        }
11244        let Some(existing) = best else {
11245            best = Some(outcome);
11246            continue;
11247        };
11248        let outcome_position = (outcome.index, outcome.consumed_eof);
11249        let best_position = (existing.index, existing.consumed_eof);
11250        let better = match prediction_mode {
11251            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
11252                outcome_position,
11253                outcome.diagnostics,
11254                best_position,
11255                existing.diagnostics,
11256                arena,
11257            ),
11258            PredictionMode::Sll => outcome.index > existing.index,
11259        };
11260        best = Some(if better { outcome } else { existing });
11261    }
11262    let should_use_caller_follow =
11263        best_caller_follow
11264            .as_ref()
11265            .zip(best.as_ref())
11266            .is_some_and(|(candidate, selected)| {
11267                if !selected.diagnostics.is_empty() {
11268                    return true;
11269                }
11270                candidate.index < selected.index
11271                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
11272            });
11273    if should_use_caller_follow {
11274        best_caller_follow
11275    } else {
11276        best
11277    }
11278}
11279
11280fn select_best_outcome(
11281    outcomes: impl Iterator<Item = RecognizeOutcome>,
11282    prediction_mode: PredictionMode,
11283    arena: &RecognitionArena,
11284) -> Option<RecognizeOutcome> {
11285    let outcomes = outcomes.collect::<Vec<_>>();
11286    let prefer_first_tie = outcomes
11287        .iter()
11288        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
11289    outcomes.into_iter().reduce(|best, outcome| {
11290        let outcome_position = (outcome.index, outcome.consumed_eof);
11291        let best_position = (best.index, best.consumed_eof);
11292        let better = match prediction_mode {
11293            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
11294                outcome_is_better(
11295                    outcome_position,
11296                    outcome.diagnostics,
11297                    best_position,
11298                    best.diagnostics,
11299                    arena,
11300                ) || (!prefer_first_tie
11301                    && outcome_position == best_position
11302                    && arena.diagnostics_len(outcome.diagnostics)
11303                        == arena.diagnostics_len(best.diagnostics)
11304                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
11305                        == arena.diagnostics_recovery_rank(best.diagnostics)
11306                    && (outcome.decisions < best.decisions
11307                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
11308            }
11309            PredictionMode::Sll => {
11310                outcome_position > best_position
11311                    || (outcome_position == best_position
11312                        && !prefer_first_tie
11313                        && (outcome.decisions < best.decisions
11314                            || (outcome.decisions == best.decisions
11315                                && outcome_is_better(
11316                                    outcome_position,
11317                                    outcome.diagnostics,
11318                                    best_position,
11319                                    best.diagnostics,
11320                                    arena,
11321                                ))))
11322            }
11323        };
11324        if better {
11325            return outcome;
11326        }
11327        best
11328    })
11329}
11330
11331/// Records the serialized transition order at parser decision states.
11332///
11333/// When two clean paths consume the same input, ANTLR's adaptive prediction
11334/// chooses by alternative order. Keeping this compact trace lets the metadata
11335/// recognizer distinguish greedy and non-greedy optional blocks without a full
11336/// prediction simulator.
11337fn transition_decision(
11338    atn: &Atn,
11339    state: AtnState<'_>,
11340    transition_count: usize,
11341    transition_index: usize,
11342    predicates: &[(usize, usize, ParserPredicate)],
11343) -> Option<usize> {
11344    if transition_count <= 1
11345        || state.precedence_rule_decision()
11346        || decision_reaches_unsupported_predicate(atn, state, predicates)
11347    {
11348        return None;
11349    }
11350    Some(transition_index)
11351}
11352
11353/// Reports whether a state should reset the active no-viable decision start.
11354///
11355/// Loop entry/back states are continuations of the surrounding adaptive
11356/// prediction; resetting at those states would turn LL-star failures back into
11357/// ordinary mismatches.
11358fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
11359    transition_count > 1
11360        && !matches!(
11361            state.kind(),
11362            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
11363        )
11364}
11365
11366/// Marks a farthest expected-token set as no-viable when multiple alternatives
11367/// failed after the active decision had already consumed input.
11368fn record_no_viable_if_ambiguous(
11369    expected: &mut ExpectedTokens,
11370    decision_start_index: Option<usize>,
11371    index: usize,
11372) {
11373    if expected.index == Some(index) && expected.symbols.len() > 1 {
11374        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11375            expected.record_no_viable(decision_start, index);
11376        }
11377    }
11378}
11379
11380/// Records a no-viable decision caused by a failed semantic predicate before
11381/// any consuming transition can contribute an expected-token set.
11382const fn record_predicate_no_viable(
11383    expected: &mut ExpectedTokens,
11384    decision_start_index: Option<usize>,
11385    index: usize,
11386) {
11387    if let Some(decision_start) = decision_start_index {
11388        expected.record_no_viable(decision_start, index);
11389    }
11390}
11391
11392/// Returns the active decision start only when the error is past that start.
11393const fn no_viable_decision_start(
11394    decision_start_index: Option<usize>,
11395    index: usize,
11396) -> Option<usize> {
11397    match decision_start_index {
11398        Some(start) if index > start => Some(start),
11399        _ => None,
11400    }
11401}
11402
11403/// Restores expected-token bookkeeping when a child rule found a clean
11404/// consuming path; failures in longer child alternatives should not pollute the
11405/// caller's final expectation set.
11406fn restore_expected(
11407    children: &[RecognizeOutcome],
11408    child_start_index: usize,
11409    expected: &mut ExpectedTokens,
11410    snapshot: ExpectedTokens,
11411    preserve_child_expected: bool,
11412) {
11413    if preserve_child_expected {
11414        return;
11415    }
11416    if children
11417        .iter()
11418        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
11419    {
11420        *expected = snapshot;
11421    }
11422}
11423
11424/// Reports whether a decision can reach a predicate the generator did not
11425/// translate. Static alternative order is unsafe for those context predicates.
11426fn decision_reaches_unsupported_predicate(
11427    atn: &Atn,
11428    state: AtnState<'_>,
11429    predicates: &[(usize, usize, ParserPredicate)],
11430) -> bool {
11431    state.transitions().iter().any(|transition| {
11432        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
11433    })
11434}
11435
11436/// Walks epsilon-like edges from one transition to find unsupported predicates.
11437fn transition_reaches_unsupported_predicate(
11438    atn: &Atn,
11439    transition: ParserTransition<'_>,
11440    predicates: &[(usize, usize, ParserPredicate)],
11441    visited: &mut BTreeSet<usize>,
11442) -> bool {
11443    match &transition.data() {
11444        Transition::Predicate {
11445            rule_index,
11446            pred_index,
11447            ..
11448        } => !predicates
11449            .iter()
11450            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
11451        Transition::Epsilon { target }
11452        | Transition::Action { target, .. }
11453        | Transition::Rule { target, .. } => {
11454            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
11455        }
11456        Transition::Precedence { .. }
11457        | Transition::Atom { .. }
11458        | Transition::Range { .. }
11459        | Transition::Set { .. }
11460        | Transition::NotSet { .. }
11461        | Transition::Wildcard { .. } => false,
11462    }
11463}
11464
11465/// Finds an unsupported predicate reachable before a consuming transition.
11466fn state_reaches_unsupported_predicate(
11467    atn: &Atn,
11468    state_number: usize,
11469    predicates: &[(usize, usize, ParserPredicate)],
11470    visited: &mut BTreeSet<usize>,
11471) -> bool {
11472    if !visited.insert(state_number) {
11473        return false;
11474    }
11475    let Some(state) = atn.state(state_number) else {
11476        return false;
11477    };
11478    state.transitions().iter().any(|transition| {
11479        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
11480    })
11481}
11482
11483/// Adds a decision step to the front of an already-recognized suffix path.
11484fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
11485    if let Some(decision) = decision {
11486        outcome.decisions.insert(0, decision);
11487    }
11488}
11489
11490fn outcome_is_better(
11491    outcome_position: (usize, bool),
11492    outcome_diagnostics: DiagnosticSeqId,
11493    best_position: (usize, bool),
11494    best_diagnostics: DiagnosticSeqId,
11495    arena: &RecognitionArena,
11496) -> bool {
11497    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
11498    let best_len = arena.diagnostics_len(best_diagnostics);
11499    outcome_position > best_position
11500        || (outcome_position == best_position
11501            && (outcome_len < best_len
11502                || (outcome_len == best_len
11503                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
11504                        < arena.diagnostics_recovery_rank(best_diagnostics))))
11505}
11506
11507fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
11508    if outcomes
11509        .iter()
11510        .any(|outcome| outcome.diagnostics.is_empty())
11511    {
11512        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11513    }
11514}
11515
11516fn discard_recovered_outcomes_if_clean_path_exists(
11517    outcomes: &mut Vec<RecognizeOutcome>,
11518    arena: &RecognitionArena,
11519) {
11520    if outcomes
11521        .iter()
11522        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
11523    {
11524        return;
11525    }
11526    if outcomes
11527        .iter()
11528        .any(|outcome| outcome.diagnostics.is_empty())
11529    {
11530        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11531    }
11532}
11533
11534/// Reports whether a recovered outcome came from an explicit predicate
11535/// fail-option and therefore should compete with shorter clean loop exits.
11536fn outcome_has_rule_failure_diagnostic(
11537    outcome: &RecognizeOutcome,
11538    arena: &RecognitionArena,
11539) -> bool {
11540    arena
11541        .diagnostics(outcome.diagnostics)
11542        .any(|diagnostic| diagnostic.message.starts_with("rule "))
11543}
11544
11545/// Removes equivalent endpoints before memoizing a state result while
11546/// preserving ATN transition-discovery order.
11547///
11548/// Outcomes are compared on observable recognition state — the input index,
11549/// EOF consumption, and diagnostics — without descending into the parse-tree
11550/// fragment carried by `nodes`. Two paths reaching the same point with
11551/// different node trees would otherwise prevent memoization from collapsing
11552/// equivalent suffixes and explode the speculative-path cache.
11553///
11554/// The first occurrence per recognition key wins, which matches ANTLR's
11555/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
11556/// transitions before loop-exit, so the first-discovered outcome carries the
11557/// greedy parse-tree fragment.
11558fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
11559    if outcomes.len() < 2 {
11560        return;
11561    }
11562    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
11563    outcomes.retain(|outcome| {
11564        seen.insert((
11565            outcome.index,
11566            outcome.consumed_eof,
11567            arena.diagnostics_len(outcome.diagnostics),
11568            arena.diagnostics_recovery_rank(outcome.diagnostics),
11569        ))
11570    });
11571}
11572
11573const FAST_OUTCOME_INLINE_KEYS: usize = 8;
11574const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
11575const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
11576const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
11577
11578#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11579enum FastOutcomeDedupStrategy {
11580    Inline,
11581    Dense,
11582    Sparse,
11583}
11584
11585impl FastOutcomeDedupScratch {
11586    fn prepare_dense(&mut self, word_count: usize) {
11587        while let Some(word_index) = self.touched_dense_words.pop() {
11588            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
11589        }
11590        if self.dense_words.len() < word_count {
11591            self.dense_words.resize(word_count, 0);
11592        }
11593    }
11594}
11595
11596fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
11597    let first_index = outcomes.first()?.index;
11598    let (min_index, max_index) = outcomes[1..].iter().fold(
11599        (first_index, first_index),
11600        |(min_index, max_index), outcome| {
11601            (min_index.min(outcome.index), max_index.max(outcome.index))
11602        },
11603    );
11604    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
11605    let bit_count = index_span.checked_mul(2)?;
11606    let word_count =
11607        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
11608    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
11609    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
11610    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
11611        .then_some((min_index, word_count))
11612}
11613
11614#[cfg(feature = "perf-counters")]
11615fn record_clean_fast_outcome_dedup(
11616    strategy: FastOutcomeDedupStrategy,
11617    input_len: usize,
11618    output_len: usize,
11619    dense_words: usize,
11620) {
11621    let counter = match strategy {
11622        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
11623        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
11624        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
11625    };
11626    perf_counters::inc(
11627        &perf_counters::OUTCOME_DEDUPE_INPUTS,
11628        u64::try_from(input_len).unwrap_or(u64::MAX),
11629    );
11630    perf_counters::inc(
11631        &perf_counters::OUTCOME_DEDUPE_REMOVED,
11632        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
11633    );
11634    perf_counters::inc(counter, 1);
11635    perf_counters::inc(
11636        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
11637        u64::try_from(dense_words).unwrap_or(u64::MAX),
11638    );
11639}
11640
11641/// Removes duplicate clean endpoints while preserving transition-discovery
11642/// order. Tiny lists stay on the stack; larger compact ranges use a direct
11643/// bitmap, and only wide sparse ranges pay for hashing.
11644fn dedupe_clean_fast_outcomes(
11645    outcomes: &mut Vec<FastRecognizeOutcome>,
11646    scratch: &mut FastOutcomeDedupScratch,
11647) -> FastOutcomeDedupStrategy {
11648    #[cfg(feature = "perf-counters")]
11649    let input_len = outcomes.len();
11650    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
11651        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
11652        let mut inline_len = 0_usize;
11653        outcomes.retain(|outcome| {
11654            let key = (outcome.index, outcome.consumed_eof);
11655            if inline_keys[..inline_len].contains(&key) {
11656                return false;
11657            }
11658            inline_keys[inline_len] = key;
11659            inline_len += 1;
11660            true
11661        });
11662        #[cfg(feature = "perf-counters")]
11663        record_clean_fast_outcome_dedup(
11664            FastOutcomeDedupStrategy::Inline,
11665            input_len,
11666            outcomes.len(),
11667            0,
11668        );
11669        return FastOutcomeDedupStrategy::Inline;
11670    }
11671
11672    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
11673        scratch.prepare_dense(word_count);
11674        outcomes.retain(|outcome| {
11675            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
11676            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
11677            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
11678            let word = &mut scratch.dense_words[word_index];
11679            if *word & bit != 0 {
11680                return false;
11681            }
11682            if *word == 0 {
11683                scratch
11684                    .touched_dense_words
11685                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
11686            }
11687            *word |= bit;
11688            true
11689        });
11690        #[cfg(feature = "perf-counters")]
11691        record_clean_fast_outcome_dedup(
11692            FastOutcomeDedupStrategy::Dense,
11693            input_len,
11694            outcomes.len(),
11695            word_count,
11696        );
11697        return FastOutcomeDedupStrategy::Dense;
11698    }
11699
11700    scratch.sparse_keys.clear();
11701    scratch.sparse_keys.reserve(outcomes.len());
11702    outcomes.retain(|outcome| {
11703        scratch
11704            .sparse_keys
11705            .insert((outcome.index, outcome.consumed_eof))
11706    });
11707    #[cfg(feature = "perf-counters")]
11708    record_clean_fast_outcome_dedup(
11709        FastOutcomeDedupStrategy::Sparse,
11710        input_len,
11711        outcomes.len(),
11712        0,
11713    );
11714    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
11715        scratch.sparse_keys = FxHashSet::default();
11716    }
11717    FastOutcomeDedupStrategy::Sparse
11718}
11719
11720/// Sorts and removes equivalent endpoints, including action traces and the
11721/// arena-backed node sequence's structural contents.
11722fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
11723    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
11724    outcomes
11725        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
11726}
11727
11728fn compare_recognize_outcomes(
11729    left: &RecognizeOutcome,
11730    right: &RecognizeOutcome,
11731    arena: &RecognitionArena,
11732) -> Ordering {
11733    left.index
11734        .cmp(&right.index)
11735        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
11736        .then_with(|| left.alt_number.cmp(&right.alt_number))
11737        .then_with(|| left.member_values.cmp(&right.member_values))
11738        .then_with(|| left.return_values.cmp(&right.return_values))
11739        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
11740        .then_with(|| left.decisions.cmp(&right.decisions))
11741        .then_with(|| left.actions.cmp(&right.actions))
11742        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
11743}
11744
11745impl<S, H> Recognizer for BaseParser<S, H>
11746where
11747    S: TokenSource,
11748    H: SemanticHooks,
11749{
11750    fn data(&self) -> &RecognizerData {
11751        &self.data
11752    }
11753
11754    fn data_mut(&mut self) -> &mut RecognizerData {
11755        &mut self.data
11756    }
11757}
11758
11759impl<S, H> Parser for BaseParser<S, H>
11760where
11761    S: TokenSource,
11762    H: SemanticHooks,
11763{
11764    fn build_parse_trees(&self) -> bool {
11765        self.build_parse_trees
11766    }
11767
11768    fn set_build_parse_trees(&mut self, build: bool) {
11769        self.build_parse_trees = build;
11770    }
11771
11772    fn number_of_syntax_errors(&self) -> usize {
11773        Self::number_of_syntax_errors(self)
11774    }
11775
11776    fn report_diagnostic_errors(&self) -> bool {
11777        self.report_diagnostic_errors
11778    }
11779
11780    fn set_report_diagnostic_errors(&mut self, report: bool) {
11781        self.report_diagnostic_errors = report;
11782    }
11783
11784    fn prediction_mode(&self) -> PredictionMode {
11785        self.prediction_mode
11786    }
11787
11788    fn set_prediction_mode(&mut self, mode: PredictionMode) {
11789        self.prediction_mode = mode;
11790    }
11791}
11792
11793#[cfg(test)]
11794mod tests {
11795    use super::*;
11796    use crate::atn::parser::{
11797        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
11798    };
11799    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
11800    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
11801    use crate::token_stream::CommonTokenStream;
11802    use crate::tree::{NodeKind, ParseTreeStats};
11803    use crate::vocabulary::Vocabulary;
11804    use std::mem::size_of;
11805
11806    #[test]
11807    fn fx_hasher_write_matches_typed_methods_for_full_words() {
11808        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
11809        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
11810        // fields) must hash the same as the typed methods, otherwise an
11811        // `FxHashMap` keyed on such a type silently disagrees with itself
11812        // depending on which entry point the caller used. Verify the
11813        // little-endian word equivalence this PR established.
11814        let value: u64 = 0x0102_0304_0506_0708;
11815        let mut typed = FxHasher::default();
11816        typed.write_u64(value);
11817        let mut bytewise = FxHasher::default();
11818        bytewise.write(&value.to_le_bytes());
11819        assert_eq!(typed.finish(), bytewise.finish());
11820    }
11821
11822    #[derive(Clone, Debug)]
11823    struct TestToken {
11824        spec: TokenSpec,
11825        id: TokenId,
11826        source_name: String,
11827    }
11828
11829    impl TestToken {
11830        fn new(token_type: i32) -> Self {
11831            Self {
11832                spec: TokenSpec::explicit(token_type, ""),
11833                id: TokenId::try_from(0).expect("zero token ID"),
11834                source_name: String::new(),
11835            }
11836        }
11837
11838        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
11839            Self {
11840                spec: TokenSpec::eof(index, index, line, column),
11841                id: TokenId::try_from(0).expect("zero token ID"),
11842                source_name: source_name.to_owned(),
11843            }
11844        }
11845
11846        fn with_text(mut self, text: impl Into<String>) -> Self {
11847            self.spec.text = Some(text.into());
11848            self
11849        }
11850
11851        const fn with_channel(mut self, channel: i32) -> Self {
11852            self.spec.channel = channel;
11853            self
11854        }
11855
11856        const fn with_span(mut self, start: usize, stop: usize) -> Self {
11857            self.spec.start = start;
11858            self.spec.stop = stop;
11859            self.spec.start_byte = start;
11860            self.spec.stop_byte = match stop.checked_add(1) {
11861                Some(end) if end >= start => end,
11862                Some(_) | None => start,
11863            };
11864            self
11865        }
11866
11867        const fn with_position(mut self, line: usize, column: usize) -> Self {
11868            self.spec.line = line;
11869            self.spec.column = column;
11870            self
11871        }
11872
11873        fn set_token_index(&mut self, index: isize) {
11874            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
11875        }
11876    }
11877
11878    impl Token for TestToken {
11879        fn token_id(&self) -> TokenId {
11880            self.id
11881        }
11882
11883        fn token_type(&self) -> i32 {
11884            self.spec.token_type
11885        }
11886
11887        fn channel(&self) -> i32 {
11888            self.spec.channel
11889        }
11890
11891        fn start(&self) -> usize {
11892            self.spec.start
11893        }
11894
11895        fn stop(&self) -> usize {
11896            self.spec.stop
11897        }
11898
11899        fn line(&self) -> usize {
11900            self.spec.line
11901        }
11902
11903        fn column(&self) -> usize {
11904            self.spec.column
11905        }
11906
11907        fn text(&self) -> Option<&str> {
11908            self.spec.text.as_deref()
11909        }
11910
11911        fn source_name(&self) -> &str {
11912            &self.source_name
11913        }
11914
11915        fn start_byte(&self) -> usize {
11916            self.spec.start_byte
11917        }
11918
11919        fn stop_byte(&self) -> usize {
11920            self.spec.stop_byte
11921        }
11922    }
11923
11924    #[derive(Debug)]
11925    struct Source {
11926        tokens: Vec<TestToken>,
11927        index: usize,
11928    }
11929
11930    impl TokenSource for Source {
11931        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
11932            let token = self
11933                .tokens
11934                .get(self.index)
11935                .cloned()
11936                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
11937            self.index += 1;
11938            sink.push(token.spec)
11939        }
11940
11941        fn line(&self) -> usize {
11942            1
11943        }
11944
11945        fn column(&self) -> usize {
11946            self.index
11947        }
11948
11949        fn source_name(&self) -> &'static str {
11950            "parser-test"
11951        }
11952    }
11953
11954    fn mini_parser_data() -> RecognizerData {
11955        RecognizerData::new(
11956            "Mini.g4",
11957            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
11958        )
11959        .with_rule_names(["s"])
11960    }
11961
11962    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
11963        let data = mini_parser_data();
11964        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
11965    }
11966
11967    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
11968    where
11969        H: SemanticHooks,
11970    {
11971        BaseParser::with_semantic_hooks(
11972            CommonTokenStream::new(Source { tokens, index: 0 }),
11973            mini_parser_data(),
11974            hooks,
11975        )
11976    }
11977
11978    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
11979        builder.finish().expect("valid packed parser ATN")
11980    }
11981
11982    fn ordinary_star_loop_atn() -> Atn {
11983        let mut atn = ParserAtnBuilder::new(2);
11984        for (state_number, kind, rule_index) in [
11985            (0, AtnStateKind::RuleStart, 0),
11986            (1, AtnStateKind::StarLoopEntry, 0),
11987            (2, AtnStateKind::Basic, 0),
11988            (3, AtnStateKind::StarLoopBack, 0),
11989            (4, AtnStateKind::LoopEnd, 0),
11990            (5, AtnStateKind::Basic, 0),
11991            (6, AtnStateKind::RuleStop, 0),
11992            (7, AtnStateKind::RuleStart, 1),
11993            (8, AtnStateKind::Basic, 1),
11994            (9, AtnStateKind::RuleStop, 1),
11995        ] {
11996            assert_eq!(
11997                atn.add_state(kind, Some(rule_index))
11998                    .expect("state")
11999                    .index(),
12000                state_number
12001            );
12002        }
12003        atn.set_rule_to_start_state(vec![0, 7])
12004            .expect("rule start states");
12005        atn.set_rule_to_stop_state(vec![6, 9])
12006            .expect("rule stop states");
12007        atn.add_decision_state(1).expect("decision state");
12008        atn.set_loop_back_state(4, 3).expect("loop back state");
12009        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12010            .expect("transition");
12011        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12012            .expect("transition");
12013        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12014            .expect("transition");
12015        atn.add_transition(
12016            2,
12017            ParserTransitionSpec::Rule {
12018                target: 7,
12019                rule_index: 1,
12020                follow_state: 3,
12021                precedence: 0,
12022            },
12023        )
12024        .expect("transition");
12025        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12026            .expect("transition");
12027        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12028            .expect("transition");
12029        atn.add_transition(
12030            5,
12031            ParserTransitionSpec::Atom {
12032                target: 6,
12033                label: TOKEN_EOF,
12034            },
12035        )
12036        .expect("transition");
12037        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12038            .expect("transition");
12039        atn.add_transition(
12040            8,
12041            ParserTransitionSpec::Atom {
12042                target: 9,
12043                label: 1,
12044            },
12045        )
12046        .expect("transition");
12047        finish_atn(atn)
12048    }
12049
12050    /// ATN for `s : (X | X X)* EOF`.
12051    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12052        let mut atn = ParserAtnBuilder::new(1);
12053        for (state_number, kind) in [
12054            (0, AtnStateKind::RuleStart),
12055            (1, AtnStateKind::StarLoopEntry),
12056            (2, AtnStateKind::StarBlockStart),
12057            (3, AtnStateKind::Basic),
12058            (4, AtnStateKind::BlockEnd),
12059            (5, AtnStateKind::StarLoopBack),
12060            (6, AtnStateKind::LoopEnd),
12061            (7, AtnStateKind::Basic),
12062            (8, AtnStateKind::RuleStop),
12063        ] {
12064            assert_eq!(
12065                atn.add_state(kind, Some(0)).expect("state").index(),
12066                state_number
12067            );
12068        }
12069        atn.set_rule_to_start_state(vec![0])
12070            .expect("rule start states");
12071        atn.set_rule_to_stop_state(vec![8])
12072            .expect("rule stop states");
12073        atn.set_end_state(2, 4).expect("block end state");
12074        atn.set_loop_back_state(6, 5).expect("loop back state");
12075        atn.add_decision_state(1).expect("decision state");
12076        atn.add_decision_state(2).expect("decision state");
12077        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12078            .expect("transition");
12079        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12080            .expect("transition");
12081        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
12082            .expect("transition");
12083        atn.add_transition(
12084            2,
12085            ParserTransitionSpec::Atom {
12086                target: 4,
12087                label: 1,
12088            },
12089        )
12090        .expect("transition");
12091        atn.add_transition(
12092            2,
12093            ParserTransitionSpec::Atom {
12094                target: 3,
12095                label: 1,
12096            },
12097        )
12098        .expect("transition");
12099        atn.add_transition(
12100            3,
12101            ParserTransitionSpec::Atom {
12102                target: 4,
12103                label: 1,
12104            },
12105        )
12106        .expect("transition");
12107        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12108            .expect("transition");
12109        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
12110            .expect("transition");
12111        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12112            .expect("transition");
12113        atn.add_transition(
12114            7,
12115            ParserTransitionSpec::Atom {
12116                target: 8,
12117                label: TOKEN_EOF,
12118            },
12119        )
12120        .expect("transition");
12121        finish_atn(atn)
12122    }
12123
12124    fn ordinary_plus_loop_atn() -> Atn {
12125        let mut atn = ParserAtnBuilder::new(2);
12126        for (state_number, kind, rule_index) in [
12127            (0, AtnStateKind::RuleStart, 0),
12128            (1, AtnStateKind::Basic, 0),
12129            (2, AtnStateKind::PlusLoopBack, 0),
12130            (3, AtnStateKind::LoopEnd, 0),
12131            (4, AtnStateKind::Basic, 0),
12132            (5, AtnStateKind::RuleStop, 0),
12133            (6, AtnStateKind::RuleStart, 1),
12134            (7, AtnStateKind::Basic, 1),
12135            (8, AtnStateKind::RuleStop, 1),
12136        ] {
12137            assert_eq!(
12138                atn.add_state(kind, Some(rule_index))
12139                    .expect("state")
12140                    .index(),
12141                state_number
12142            );
12143        }
12144        atn.set_rule_to_start_state(vec![0, 6])
12145            .expect("rule start states");
12146        atn.set_rule_to_stop_state(vec![5, 8])
12147            .expect("rule stop states");
12148        atn.add_decision_state(2).expect("decision state");
12149        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12150            .expect("transition");
12151        atn.add_transition(
12152            1,
12153            ParserTransitionSpec::Rule {
12154                target: 6,
12155                rule_index: 1,
12156                follow_state: 2,
12157                precedence: 0,
12158            },
12159        )
12160        .expect("transition");
12161        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
12162            .expect("transition");
12163        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12164            .expect("transition");
12165        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
12166            .expect("transition");
12167        atn.add_transition(
12168            4,
12169            ParserTransitionSpec::Atom {
12170                target: 5,
12171                label: TOKEN_EOF,
12172            },
12173        )
12174        .expect("transition");
12175        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12176            .expect("transition");
12177        atn.add_transition(
12178            7,
12179            ParserTransitionSpec::Atom {
12180                target: 8,
12181                label: 1,
12182            },
12183        )
12184        .expect("transition");
12185        finish_atn(atn)
12186    }
12187
12188    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
12189        let mut tokens = (0..count)
12190            .map(|_| TestToken::new(1).with_text("x"))
12191            .collect::<Vec<_>>();
12192        tokens.push(TestToken::eof("parser-test", count, 1, count));
12193        tokens
12194    }
12195
12196    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
12197        let mut atn = ParserAtnBuilder::new(2);
12198        assert_eq!(
12199            atn.add_state(AtnStateKind::RuleStart, Some(0))
12200                .expect("state")
12201                .index(),
12202            0
12203        );
12204        assert_eq!(
12205            atn.add_state(AtnStateKind::Basic, Some(0))
12206                .expect("state")
12207                .index(),
12208            1
12209        );
12210        assert_eq!(
12211            atn.add_state(AtnStateKind::Basic, Some(0))
12212                .expect("state")
12213                .index(),
12214            2
12215        );
12216        assert_eq!(
12217            atn.add_state(AtnStateKind::RuleStart, Some(1))
12218                .expect("state")
12219                .index(),
12220            3
12221        );
12222        atn.set_left_recursive_rule(3)
12223            .expect("left-recursive rule start");
12224        assert_eq!(
12225            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
12226                .expect("state")
12227                .index(),
12228            4
12229        );
12230        atn.set_precedence_rule_decision(4)
12231            .expect("precedence decision");
12232        assert_eq!(
12233            atn.add_state(AtnStateKind::Basic, Some(1))
12234                .expect("state")
12235                .index(),
12236            5
12237        );
12238        assert_eq!(
12239            atn.add_state(AtnStateKind::Basic, Some(1))
12240                .expect("state")
12241                .index(),
12242            6
12243        );
12244        assert_eq!(
12245            atn.add_state(AtnStateKind::LoopEnd, Some(1))
12246                .expect("state")
12247                .index(),
12248            7
12249        );
12250        assert_eq!(
12251            atn.add_state(AtnStateKind::RuleStop, Some(1))
12252                .expect("state")
12253                .index(),
12254            8
12255        );
12256        assert_eq!(
12257            atn.add_state(AtnStateKind::RuleStop, Some(0))
12258                .expect("state")
12259                .index(),
12260            9
12261        );
12262        atn.set_rule_to_start_state(vec![0, 3])
12263            .expect("rule start states");
12264        atn.set_rule_to_stop_state(vec![9, 8])
12265            .expect("rule stop states");
12266        atn.add_transition(
12267            1,
12268            ParserTransitionSpec::Rule {
12269                target: 3,
12270                rule_index: 1,
12271                follow_state: 2,
12272                precedence: 0,
12273            },
12274        )
12275        .expect("transition");
12276        atn.add_transition(
12277            2,
12278            ParserTransitionSpec::Atom {
12279                target: 9,
12280                label: caller_symbol,
12281            },
12282        )
12283        .expect("transition");
12284        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12285            .expect("transition");
12286        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
12287            .expect("transition");
12288        atn.add_transition(
12289            5,
12290            ParserTransitionSpec::Precedence {
12291                target: 6,
12292                precedence: 1,
12293            },
12294        )
12295        .expect("transition");
12296        atn.add_transition(
12297            6,
12298            ParserTransitionSpec::Atom {
12299                target: 4,
12300                label: 1,
12301            },
12302        )
12303        .expect("transition");
12304        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12305            .expect("transition");
12306        finish_atn(atn)
12307    }
12308
12309    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
12310        let mut parser = mini_parser(vec![
12311            TestToken::new(symbol).with_text("lookahead"),
12312            TestToken::eof("parser-test", 1, 1, 1),
12313        ]);
12314        parser.rule_context_stack = vec![
12315            RuleContextFrame {
12316                rule_index: 0,
12317                invoking_state: -1,
12318            },
12319            RuleContextFrame {
12320                rule_index: 1,
12321                invoking_state: 1,
12322            },
12323        ];
12324        parser
12325    }
12326
12327    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
12328        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
12329        //   prec 2: token 1, token 1  (shift `>>`)
12330        //   prec 1: token 1           (relational `>`)
12331        let mut atn = ParserAtnBuilder::new(1);
12332        for (state, kind, rule) in [
12333            (0, AtnStateKind::RuleStart, 0),
12334            (1, AtnStateKind::StarLoopEntry, 0),
12335            (2, AtnStateKind::Basic, 0), // ops hub
12336            (3, AtnStateKind::Basic, 0), // shift prec
12337            (4, AtnStateKind::Basic, 0), // shift first >
12338            (5, AtnStateKind::Basic, 0), // shift second >
12339            (6, AtnStateKind::Basic, 0), // rel prec
12340            (7, AtnStateKind::Basic, 0), // rel >
12341            (8, AtnStateKind::LoopEnd, 0),
12342            (9, AtnStateKind::RuleStop, 0),
12343        ] {
12344            assert_eq!(
12345                atn.add_state(kind, Some(rule)).expect("state").index(),
12346                state
12347            );
12348            if state == 0 {
12349                atn.set_left_recursive_rule(state)
12350                    .expect("left-recursive rule start");
12351            } else if state == 1 {
12352                atn.set_precedence_rule_decision(state)
12353                    .expect("precedence decision");
12354            }
12355        }
12356        atn.set_rule_to_start_state(vec![0])
12357            .expect("rule start states");
12358        atn.set_rule_to_stop_state(vec![9])
12359            .expect("rule stop states");
12360        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12361            .expect("ops");
12362        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12363            .expect("exit");
12364        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12365            .expect("to shift");
12366        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12367            .expect("to rel");
12368        atn.add_transition(
12369            3,
12370            ParserTransitionSpec::Precedence {
12371                target: 4,
12372                precedence: 2,
12373            },
12374        )
12375        .expect("shift prec");
12376        atn.add_transition(
12377            4,
12378            ParserTransitionSpec::Atom {
12379                target: 5,
12380                label: 1,
12381            },
12382        )
12383        .expect("shift first >");
12384        atn.add_transition(
12385            5,
12386            ParserTransitionSpec::Atom {
12387                target: 1,
12388                label: 1,
12389            },
12390        )
12391        .expect("shift second >");
12392        atn.add_transition(
12393            6,
12394            ParserTransitionSpec::Precedence {
12395                target: 7,
12396                precedence: 1,
12397            },
12398        )
12399        .expect("rel prec");
12400        atn.add_transition(
12401            7,
12402            ParserTransitionSpec::Atom {
12403                target: 1,
12404                label: 1,
12405            },
12406        )
12407        .expect("rel >");
12408        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12409            .expect("loop end");
12410        finish_atn(atn)
12411    }
12412
12413    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
12414        let mut atn = ParserAtnBuilder::new(2);
12415        for (state, kind, rule) in [
12416            (0, AtnStateKind::RuleStart, 0),
12417            (1, AtnStateKind::StarLoopEntry, 0),
12418            (2, AtnStateKind::Basic, 0),
12419            (3, AtnStateKind::Basic, 0),
12420            (4, AtnStateKind::Basic, 0),
12421            (5, AtnStateKind::Basic, 0),
12422            (6, AtnStateKind::Basic, 0),
12423            (7, AtnStateKind::Basic, 0),
12424            (8, AtnStateKind::LoopEnd, 0),
12425            (9, AtnStateKind::RuleStop, 0),
12426            (10, AtnStateKind::RuleStart, 1),
12427            (11, AtnStateKind::Basic, 1),
12428            (12, AtnStateKind::RuleStop, 1),
12429        ] {
12430            assert_eq!(
12431                atn.add_state(kind, Some(rule)).expect("state").index(),
12432                state
12433            );
12434            if state == 0 {
12435                atn.set_left_recursive_rule(state)
12436                    .expect("left-recursive rule start");
12437            } else if state == 1 {
12438                atn.set_precedence_rule_decision(state)
12439                    .expect("precedence decision");
12440            }
12441        }
12442        atn.set_rule_to_start_state(vec![0, 10])
12443            .expect("rule start states");
12444        atn.set_rule_to_stop_state(vec![9, 12])
12445            .expect("rule stop states");
12446        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12447            .expect("ops");
12448        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12449            .expect("exit");
12450        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12451            .expect("to shift");
12452        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12453            .expect("to relational");
12454        atn.add_transition(
12455            3,
12456            ParserTransitionSpec::Precedence {
12457                target: 4,
12458                precedence: 2,
12459            },
12460        )
12461        .expect("shift precedence");
12462        atn.add_transition(
12463            4,
12464            ParserTransitionSpec::Rule {
12465                target: 10,
12466                rule_index: 1,
12467                follow_state: 5,
12468                precedence: 0,
12469            },
12470        )
12471        .expect("first shift token helper");
12472        atn.add_transition(
12473            5,
12474            ParserTransitionSpec::Atom {
12475                target: 1,
12476                label: 1,
12477            },
12478        )
12479        .expect("second shift token");
12480        atn.add_transition(
12481            6,
12482            ParserTransitionSpec::Precedence {
12483                target: 7,
12484                precedence: 1,
12485            },
12486        )
12487        .expect("relational precedence");
12488        atn.add_transition(
12489            7,
12490            ParserTransitionSpec::Atom {
12491                target: 1,
12492                label: 1,
12493            },
12494        )
12495        .expect("relational token");
12496        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12497            .expect("loop end");
12498        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
12499            .expect("helper entry");
12500        atn.add_transition(
12501            11,
12502            ParserTransitionSpec::Atom {
12503                target: 12,
12504                label: 1,
12505            },
12506        )
12507        .expect("first shift token");
12508        finish_atn(atn)
12509    }
12510
12511    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
12512        let mut atn = ParserAtnBuilder::new(1);
12513        for (state, kind) in [
12514            (0, AtnStateKind::RuleStart),
12515            (1, AtnStateKind::StarLoopEntry),
12516            (2, AtnStateKind::Basic),
12517            (3, AtnStateKind::Basic),
12518            (4, AtnStateKind::Basic),
12519            (5, AtnStateKind::Basic),
12520            (6, AtnStateKind::Basic),
12521            (7, AtnStateKind::Basic),
12522            (8, AtnStateKind::Basic),
12523            (9, AtnStateKind::LoopEnd),
12524            (10, AtnStateKind::RuleStop),
12525        ] {
12526            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
12527            if state == 0 {
12528                atn.set_left_recursive_rule(state)
12529                    .expect("left-recursive rule start");
12530            } else if state == 1 {
12531                atn.set_precedence_rule_decision(state)
12532                    .expect("precedence decision");
12533            }
12534        }
12535        atn.set_rule_to_start_state(vec![0])
12536            .expect("rule start states");
12537        atn.set_rule_to_stop_state(vec![10])
12538            .expect("rule stop states");
12539        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12540            .expect("ops");
12541        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
12542            .expect("exit");
12543        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12544            .expect("to multi-token operator");
12545        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12546            .expect("to predicate operator");
12547        atn.add_transition(
12548            3,
12549            ParserTransitionSpec::Precedence {
12550                target: 4,
12551                precedence: 2,
12552            },
12553        )
12554        .expect("multi-token precedence");
12555        atn.add_transition(
12556            4,
12557            ParserTransitionSpec::Atom {
12558                target: 5,
12559                label: 1,
12560            },
12561        )
12562        .expect("multi-token first");
12563        atn.add_transition(
12564            5,
12565            ParserTransitionSpec::Atom {
12566                target: 1,
12567                label: 1,
12568            },
12569        )
12570        .expect("multi-token second");
12571        atn.add_transition(
12572            6,
12573            ParserTransitionSpec::Precedence {
12574                target: 7,
12575                precedence: 2,
12576            },
12577        )
12578        .expect("predicate precedence");
12579        atn.add_transition(
12580            7,
12581            ParserTransitionSpec::Predicate {
12582                target: 8,
12583                rule_index: 0,
12584                pred_index: 0,
12585                context_dependent: false,
12586            },
12587        )
12588        .expect("operator predicate");
12589        atn.add_transition(
12590            8,
12591            ParserTransitionSpec::Atom {
12592                target: 1,
12593                label: 1,
12594            },
12595        )
12596        .expect("predicate single token");
12597        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
12598            .expect("loop end");
12599        finish_atn(atn)
12600    }
12601
12602    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
12603        let mut atn = ParserAtnBuilder::new(2);
12604        for (state, kind, rule) in [
12605            (0, AtnStateKind::RuleStart, 0),
12606            (1, AtnStateKind::StarLoopEntry, 0),
12607            (2, AtnStateKind::Basic, 0),
12608            (3, AtnStateKind::Basic, 0),
12609            (4, AtnStateKind::Basic, 0),
12610            (5, AtnStateKind::LoopEnd, 0),
12611            (6, AtnStateKind::RuleStop, 0),
12612            (7, AtnStateKind::RuleStart, 1),
12613            (8, AtnStateKind::RuleStop, 1),
12614            (9, AtnStateKind::Basic, 1),
12615        ] {
12616            assert_eq!(
12617                atn.add_state(kind, Some(rule)).expect("state").index(),
12618                state
12619            );
12620            if state == 0 {
12621                atn.set_left_recursive_rule(state)
12622                    .expect("left-recursive rule start");
12623            } else if state == 1 {
12624                atn.set_precedence_rule_decision(state)
12625                    .expect("precedence decision");
12626            }
12627        }
12628        atn.set_rule_to_start_state(vec![0, 7])
12629            .expect("rule start states");
12630        atn.set_rule_to_stop_state(vec![6, 8])
12631            .expect("rule stop states");
12632        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12633            .expect("transition");
12634        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
12635            .expect("transition");
12636        atn.add_transition(
12637            2,
12638            ParserTransitionSpec::Precedence {
12639                target: 3,
12640                precedence: 3,
12641            },
12642        )
12643        .expect("transition");
12644        atn.add_transition(
12645            3,
12646            ParserTransitionSpec::Rule {
12647                target: 7,
12648                rule_index: 1,
12649                follow_state: 4,
12650                precedence: 0,
12651            },
12652        )
12653        .expect("transition");
12654        atn.add_transition(
12655            4,
12656            ParserTransitionSpec::Atom {
12657                target: 1,
12658                label: 1,
12659            },
12660        )
12661        .expect("transition");
12662        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
12663            .expect("transition");
12664        atn.add_transition(
12665            7,
12666            ParserTransitionSpec::Precedence {
12667                target: 9,
12668                precedence: 1,
12669            },
12670        )
12671        .expect("transition");
12672        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
12673            .expect("transition");
12674        finish_atn(atn)
12675    }
12676
12677    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
12678        let mut atn = ParserAtnBuilder::new(2);
12679        for (state, kind) in [
12680            (0, AtnStateKind::RuleStart),
12681            (1, AtnStateKind::StarLoopEntry),
12682            (2, AtnStateKind::Basic),
12683            (3, AtnStateKind::Basic),
12684            (4, AtnStateKind::Basic),
12685            (5, AtnStateKind::LoopEnd),
12686            (6, AtnStateKind::RuleStop),
12687        ] {
12688            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
12689            if state == 0 {
12690                atn.set_left_recursive_rule(state)
12691                    .expect("left-recursive rule start");
12692            } else if state == 1 {
12693                atn.set_precedence_rule_decision(state)
12694                    .expect("precedence decision");
12695            }
12696        }
12697        atn.set_rule_to_start_state(vec![0])
12698            .expect("rule start states");
12699        atn.set_rule_to_stop_state(vec![6])
12700            .expect("rule stop states");
12701        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12702            .expect("transition");
12703        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
12704            .expect("transition");
12705        atn.add_transition(
12706            2,
12707            ParserTransitionSpec::Precedence {
12708                target: 3,
12709                precedence: 1,
12710            },
12711        )
12712        .expect("transition");
12713        atn.add_transition(
12714            3,
12715            ParserTransitionSpec::Predicate {
12716                target: 4,
12717                rule_index: 0,
12718                pred_index: 0,
12719                context_dependent: false,
12720            },
12721        )
12722        .expect("transition");
12723        atn.add_transition(
12724            4,
12725            ParserTransitionSpec::Atom {
12726                target: 1,
12727                label: 1,
12728            },
12729        )
12730        .expect("transition");
12731        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
12732            .expect("transition");
12733        finish_atn(atn)
12734    }
12735
12736    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
12737        let mut atn = ParserAtnBuilder::new(2);
12738        for (state, kind, rule) in [
12739            (0, AtnStateKind::RuleStart, 0),
12740            (1, AtnStateKind::Basic, 0),
12741            (2, AtnStateKind::Basic, 0),
12742            (3, AtnStateKind::Basic, 0),
12743            (4, AtnStateKind::RuleStop, 0),
12744            (5, AtnStateKind::RuleStart, 1),
12745            (6, AtnStateKind::StarLoopEntry, 1),
12746            (7, AtnStateKind::Basic, 1),
12747            (8, AtnStateKind::Basic, 1),
12748            (9, AtnStateKind::LoopEnd, 1),
12749            (10, AtnStateKind::RuleStop, 1),
12750            (11, AtnStateKind::RuleStart, 2),
12751            (12, AtnStateKind::RuleStop, 2),
12752        ] {
12753            assert_eq!(
12754                atn.add_state(kind, Some(rule)).expect("state").index(),
12755                state
12756            );
12757            if state == 5 {
12758                atn.set_left_recursive_rule(state)
12759                    .expect("left-recursive rule start");
12760            } else if state == 6 {
12761                atn.set_precedence_rule_decision(state)
12762                    .expect("precedence decision");
12763            }
12764        }
12765        atn.set_rule_to_start_state(vec![0, 5, 11])
12766            .expect("rule start states");
12767        atn.set_rule_to_stop_state(vec![4, 10, 12])
12768            .expect("rule stop states");
12769        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12770            .expect("transition");
12771        atn.add_transition(
12772            1,
12773            ParserTransitionSpec::Rule {
12774                target: 5,
12775                rule_index: 1,
12776                follow_state: 2,
12777                precedence: 0,
12778            },
12779        )
12780        .expect("transition");
12781        atn.add_transition(
12782            2,
12783            ParserTransitionSpec::Rule {
12784                target: 11,
12785                rule_index: 2,
12786                follow_state: 3,
12787                precedence: 0,
12788            },
12789        )
12790        .expect("transition");
12791        atn.add_transition(
12792            3,
12793            ParserTransitionSpec::Atom {
12794                target: 4,
12795                label: caller_symbol,
12796            },
12797        )
12798        .expect("transition");
12799        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12800            .expect("transition");
12801        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
12802            .expect("transition");
12803        atn.add_transition(
12804            7,
12805            ParserTransitionSpec::Precedence {
12806                target: 8,
12807                precedence: 1,
12808            },
12809        )
12810        .expect("transition");
12811        atn.add_transition(
12812            8,
12813            ParserTransitionSpec::Atom {
12814                target: 6,
12815                label: 1,
12816            },
12817        )
12818        .expect("transition");
12819        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
12820            .expect("transition");
12821        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
12822            .expect("transition");
12823        finish_atn(atn)
12824    }
12825
12826    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
12827        let mut atn = ParserAtnBuilder::new(2);
12828        for (state, kind, rule) in [
12829            (0, AtnStateKind::RuleStart, 0),
12830            (1, AtnStateKind::Basic, 0),
12831            (2, AtnStateKind::Basic, 0),
12832            (3, AtnStateKind::RuleStop, 0),
12833            (4, AtnStateKind::RuleStart, 1),
12834            (5, AtnStateKind::Basic, 1),
12835            (6, AtnStateKind::Basic, 1),
12836            (7, AtnStateKind::RuleStop, 1),
12837            (8, AtnStateKind::RuleStart, 2),
12838            (9, AtnStateKind::StarLoopEntry, 2),
12839            (10, AtnStateKind::Basic, 2),
12840            (11, AtnStateKind::Basic, 2),
12841            (12, AtnStateKind::LoopEnd, 2),
12842            (13, AtnStateKind::RuleStop, 2),
12843        ] {
12844            assert_eq!(
12845                atn.add_state(kind, Some(rule)).expect("state").index(),
12846                state
12847            );
12848            if state == 8 {
12849                atn.set_left_recursive_rule(state)
12850                    .expect("left-recursive rule start");
12851            } else if state == 9 {
12852                atn.set_precedence_rule_decision(state)
12853                    .expect("precedence decision");
12854            }
12855        }
12856        atn.set_rule_to_start_state(vec![0, 4, 8])
12857            .expect("rule start states");
12858        atn.set_rule_to_stop_state(vec![3, 7, 13])
12859            .expect("rule stop states");
12860        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12861            .expect("transition");
12862        atn.add_transition(
12863            1,
12864            ParserTransitionSpec::Rule {
12865                target: 4,
12866                rule_index: 1,
12867                follow_state: 2,
12868                precedence: 0,
12869            },
12870        )
12871        .expect("transition");
12872        atn.add_transition(
12873            2,
12874            ParserTransitionSpec::Atom {
12875                target: 3,
12876                label: caller_symbol,
12877            },
12878        )
12879        .expect("transition");
12880        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12881            .expect("transition");
12882        atn.add_transition(
12883            5,
12884            ParserTransitionSpec::Rule {
12885                target: 8,
12886                rule_index: 2,
12887                follow_state: 6,
12888                precedence: 0,
12889            },
12890        )
12891        .expect("transition");
12892        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12893            .expect("transition");
12894        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
12895            .expect("transition");
12896        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
12897            .expect("transition");
12898        atn.add_transition(
12899            10,
12900            ParserTransitionSpec::Precedence {
12901                target: 11,
12902                precedence: 1,
12903            },
12904        )
12905        .expect("transition");
12906        atn.add_transition(
12907            11,
12908            ParserTransitionSpec::Atom {
12909                target: 9,
12910                label: 1,
12911            },
12912        )
12913        .expect("transition");
12914        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
12915            .expect("transition");
12916        finish_atn(atn)
12917    }
12918
12919    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
12920        let mut atn = ParserAtnBuilder::new(2);
12921        for (state, kind, rule) in [
12922            (0, AtnStateKind::RuleStart, 0),
12923            (1, AtnStateKind::Basic, 0),
12924            (2, AtnStateKind::Basic, 0),
12925            (3, AtnStateKind::RuleStop, 0),
12926            (4, AtnStateKind::RuleStart, 1),
12927            (5, AtnStateKind::StarLoopEntry, 1),
12928            (6, AtnStateKind::Basic, 1),
12929            (7, AtnStateKind::Basic, 1),
12930            (8, AtnStateKind::Basic, 1),
12931            (9, AtnStateKind::Basic, 1),
12932            (10, AtnStateKind::LoopEnd, 1),
12933            (11, AtnStateKind::RuleStop, 1),
12934        ] {
12935            assert_eq!(
12936                atn.add_state(kind, Some(rule)).expect("state").index(),
12937                state
12938            );
12939            if state == 4 {
12940                atn.set_left_recursive_rule(state)
12941                    .expect("left-recursive rule start");
12942            } else if state == 5 {
12943                atn.set_precedence_rule_decision(state)
12944                    .expect("precedence decision");
12945            }
12946        }
12947        atn.set_rule_to_start_state(vec![0, 4])
12948            .expect("rule start states");
12949        atn.set_rule_to_stop_state(vec![3, 11])
12950            .expect("rule stop states");
12951        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12952            .expect("transition");
12953        atn.add_transition(
12954            1,
12955            ParserTransitionSpec::Rule {
12956                target: 4,
12957                rule_index: 1,
12958                follow_state: 2,
12959                precedence: 0,
12960            },
12961        )
12962        .expect("transition");
12963        atn.add_transition(
12964            2,
12965            ParserTransitionSpec::Atom {
12966                target: 3,
12967                label: caller_symbol,
12968            },
12969        )
12970        .expect("transition");
12971        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
12972            .expect("transition");
12973        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
12974            .expect("transition");
12975        atn.add_transition(
12976            6,
12977            ParserTransitionSpec::Precedence {
12978                target: 7,
12979                precedence: 1,
12980            },
12981        )
12982        .expect("transition");
12983        atn.add_transition(
12984            7,
12985            ParserTransitionSpec::Atom {
12986                target: 8,
12987                label: 1,
12988            },
12989        )
12990        .expect("transition");
12991        atn.add_transition(
12992            8,
12993            ParserTransitionSpec::Rule {
12994                target: 4,
12995                rule_index: 1,
12996                follow_state: 9,
12997                precedence: 2,
12998            },
12999        )
13000        .expect("transition");
13001        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13002            .expect("transition");
13003        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13004            .expect("transition");
13005        finish_atn(atn)
13006    }
13007
13008    #[test]
13009    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13010        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13011        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13012
13013        let mut overlapping = parser_inside_left_recursive_callee(1);
13014        assert_eq!(
13015            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13016            None
13017        );
13018
13019        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13020        assert_eq!(
13021            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13022            Some(true)
13023        );
13024
13025        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13026        assert_eq!(
13027            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13028            Some(false)
13029        );
13030
13031        assert_eq!(
13032            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13033            Some(true),
13034            "overlap results must not leak across ATNs"
13035        );
13036    }
13037
13038    #[test]
13039    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
13040        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
13041        let mut parser = mini_parser(vec![
13042            TestToken::new(1).with_text("operator"),
13043            TestToken::eof("parser-test", 1, 1, 1),
13044        ]);
13045        parser.rule_context_stack = vec![RuleContextFrame {
13046            rule_index: 0,
13047            invoking_state: -1,
13048        }];
13049
13050        assert_eq!(
13051            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13052            Some(true)
13053        );
13054        assert_eq!(
13055            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13056            Some(true),
13057            "cached operator lookahead must preserve the nullable prefix return path"
13058        );
13059        assert_eq!(
13060            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13061            Some(true),
13062            "the nullable child must use its rule-call precedence, not the caller precedence"
13063        );
13064    }
13065
13066    #[test]
13067    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
13068        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
13069        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
13070        // must defer so StarLoopEntry adaptive predict can exit when the second
13071        // `>` is absent (as in `a < b > c`).
13072        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
13073        let mut parser = mini_parser(vec![
13074            TestToken::new(1).with_text(">"),
13075            TestToken::new(2).with_text("id"),
13076            TestToken::eof("parser-test", 1, 1, 1),
13077        ]);
13078        parser.rule_context_stack = vec![RuleContextFrame {
13079            rule_index: 0,
13080            invoking_state: -1,
13081        }];
13082
13083        assert_eq!(
13084            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13085            Some(true),
13086            "at low precedence relational `>` is a single-token operator"
13087        );
13088        assert_eq!(
13089            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
13090            Some(true),
13091            "relational remains single-token at its own precedence"
13092        );
13093        assert_eq!(
13094            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13095            None,
13096            "at shift precedence, bare `>` must not force enter"
13097        );
13098    }
13099
13100    #[test]
13101    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
13102        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
13103        let mut parser = mini_parser(vec![
13104            TestToken::new(1).with_text(">"),
13105            TestToken::new(2).with_text("id"),
13106            TestToken::eof("parser-test", 1, 1, 1),
13107        ]);
13108        parser.rule_context_stack = vec![RuleContextFrame {
13109            rule_index: 0,
13110            invoking_state: -1,
13111        }];
13112
13113        assert_eq!(
13114            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13115            Some(true),
13116            "the direct relational alternative remains a one-token operator"
13117        );
13118        assert_eq!(
13119            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13120            None,
13121            "a token matched in the helper rule must return to the second shift token"
13122        );
13123    }
13124
13125    #[test]
13126    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
13127        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
13128        let mut parser = mini_parser(vec![
13129            TestToken::new(1).with_text(">"),
13130            TestToken::new(2).with_text("id"),
13131            TestToken::eof("parser-test", 1, 1, 1),
13132        ]);
13133        parser.rule_context_stack = vec![RuleContextFrame {
13134            rule_index: 0,
13135            invoking_state: -1,
13136        }];
13137
13138        assert_eq!(
13139            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13140            None,
13141            "a predicate-gated single-token path must not be hidden by a multi-token path"
13142        );
13143    }
13144
13145    #[test]
13146    fn left_recursive_loop_defers_predicate_guarded_operator() {
13147        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
13148        let mut parser = mini_parser_with_hooks(
13149            vec![
13150                TestToken::new(1).with_text("operator"),
13151                TestToken::eof("parser-test", 1, 1, 1),
13152            ],
13153            RejectingPredicateHooks::default(),
13154        );
13155        parser.rule_context_stack = vec![RuleContextFrame {
13156            rule_index: 0,
13157            invoking_state: -1,
13158        }];
13159
13160        assert_eq!(
13161            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13162            None,
13163            "a false predicate must be evaluated before entering the operator alternative"
13164        );
13165        assert_eq!(
13166            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13167            None,
13168            "cached predicate-dependent lookahead must keep deferring"
13169        );
13170    }
13171
13172    #[test]
13173    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
13174        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
13175        let mut parser = parser_inside_left_recursive_callee(1);
13176
13177        assert_eq!(
13178            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13179            None
13180        );
13181        assert_eq!(
13182            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13183            None,
13184            "the cached overlap must preserve the nullable child return path"
13185        );
13186    }
13187
13188    #[test]
13189    fn left_recursive_loop_defers_through_nullable_parent_return() {
13190        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
13191        let mut parser = mini_parser(vec![
13192            TestToken::new(1).with_text("lookahead"),
13193            TestToken::eof("parser-test", 1, 1, 1),
13194        ]);
13195        parser.rule_context_stack = vec![
13196            RuleContextFrame {
13197                rule_index: 0,
13198                invoking_state: -1,
13199            },
13200            RuleContextFrame {
13201                rule_index: 1,
13202                invoking_state: 1,
13203            },
13204            RuleContextFrame {
13205                rule_index: 2,
13206                invoking_state: 5,
13207            },
13208        ];
13209
13210        assert_eq!(
13211            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13212            None,
13213            "a nullable caller must unwind to its parent's consuming follow path"
13214        );
13215        assert_eq!(
13216            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13217            None,
13218            "the caller-overlap cache must not retain a false negative"
13219        );
13220    }
13221
13222    #[test]
13223    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
13224        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
13225        let mut parser = mini_parser(vec![
13226            TestToken::new(1).with_text("lookahead"),
13227            TestToken::eof("parser-test", 1, 1, 1),
13228        ]);
13229        parser.rule_context_stack = vec![
13230            RuleContextFrame {
13231                rule_index: 0,
13232                invoking_state: -1,
13233            },
13234            RuleContextFrame {
13235                rule_index: 1,
13236                invoking_state: 1,
13237            },
13238            RuleContextFrame {
13239                rule_index: 1,
13240                invoking_state: 8,
13241            },
13242        ];
13243
13244        assert_eq!(
13245            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13246            None,
13247            "a recursive operand return must preserve its parent caller context"
13248        );
13249        assert_eq!(
13250            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13251            None,
13252            "the caller-overlap cache must preserve the loop-boundary return"
13253        );
13254    }
13255
13256    fn token_then_eof_atn() -> Atn {
13257        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13258            4, 1, 2, // version, parser, max token type
13259            3, // states
13260            2, 0, // rule start
13261            1, 0, // basic
13262            7, 0, // rule stop
13263            0, // non-greedy states
13264            0, // precedence states
13265            1, // rules
13266            0, // rule 0 start
13267            0, // modes
13268            0, // sets
13269            2, // transitions
13270            0, 1, 5, 1, 0, 0, // match token 1
13271            1, 2, 5, -1, 0, 0, // match EOF
13272            0, // decisions
13273        ]))
13274        .deserialize_parser()
13275        .expect("artificial parser ATN should deserialize")
13276    }
13277
13278    fn eof_then_action_atn() -> Atn {
13279        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13280            4, 1, 1, // version, parser, max token type
13281            3, // states
13282            2, 0, // rule start
13283            1, 0, // basic
13284            7, 0, // rule stop
13285            0, // non-greedy states
13286            0, // precedence states
13287            1, // rules
13288            0, // rule 0 start
13289            0, // modes
13290            0, // sets
13291            2, // transitions
13292            0, 1, 5, -1, 0, 0, // match EOF
13293            1, 2, 6, 0, 0, 0, // parser action
13294            0, // decisions
13295        ]))
13296        .deserialize_parser()
13297        .expect("artificial parser ATN should deserialize")
13298    }
13299
13300    fn noop_action_then_token_then_eof_atn() -> Atn {
13301        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13302            4, 1, 2, // version, parser, max token type
13303            4, // states
13304            2, 0, // rule start
13305            1, 0, // basic
13306            1, 0, // basic
13307            7, 0, // rule stop
13308            0, // non-greedy states
13309            0, // precedence states
13310            1, // rules
13311            0, // rule 0 start
13312            0, // modes
13313            0, // sets
13314            3, // transitions
13315            0, 1, 6, 0, -1, 0, // no-op parser action
13316            1, 2, 5, 1, 0, 0, // match token 1
13317            2, 3, 5, -1, 0, 0, // match EOF
13318            0, // decisions
13319        ]))
13320        .deserialize_parser()
13321        .expect("artificial no-op action ATN should deserialize")
13322    }
13323
13324    fn two_alt_decision_atn() -> Atn {
13325        let mut atn = ParserAtnBuilder::new(2);
13326        assert_eq!(
13327            atn.add_state(AtnStateKind::RuleStart, Some(0))
13328                .expect("state")
13329                .index(),
13330            0
13331        );
13332        assert_eq!(
13333            atn.add_state(AtnStateKind::BlockStart, Some(0))
13334                .expect("state")
13335                .index(),
13336            1
13337        );
13338        assert_eq!(
13339            atn.add_state(AtnStateKind::Basic, Some(0))
13340                .expect("state")
13341                .index(),
13342            2
13343        );
13344        assert_eq!(
13345            atn.add_state(AtnStateKind::Basic, Some(0))
13346                .expect("state")
13347                .index(),
13348            3
13349        );
13350        assert_eq!(
13351            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13352                .expect("state")
13353                .index(),
13354            4
13355        );
13356        assert_eq!(
13357            atn.add_state(AtnStateKind::RuleStop, Some(0))
13358                .expect("state")
13359                .index(),
13360            5
13361        );
13362        atn.set_rule_to_start_state(vec![0])
13363            .expect("rule start states");
13364        atn.set_rule_to_stop_state(vec![5])
13365            .expect("rule stop states");
13366        atn.add_decision_state(1).expect("decision state");
13367        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13368            .expect("transition");
13369        atn.add_transition(
13370            1,
13371            ParserTransitionSpec::Atom {
13372                target: 2,
13373                label: 1,
13374            },
13375        )
13376        .expect("transition");
13377        atn.add_transition(
13378            1,
13379            ParserTransitionSpec::Atom {
13380                target: 3,
13381                label: 2,
13382            },
13383        )
13384        .expect("transition");
13385        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
13386            .expect("transition");
13387        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13388            .expect("transition");
13389        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13390            .expect("transition");
13391        finish_atn(atn)
13392    }
13393
13394    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
13395    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
13396    fn optional_then_b_eof_atn() -> Atn {
13397        let mut atn = ParserAtnBuilder::new(3);
13398        assert_eq!(
13399            atn.add_state(AtnStateKind::RuleStart, Some(0))
13400                .expect("state")
13401                .index(),
13402            0
13403        );
13404        assert_eq!(
13405            atn.add_state(AtnStateKind::BlockStart, Some(0))
13406                .expect("state")
13407                .index(),
13408            1
13409        );
13410        assert_eq!(
13411            atn.add_state(AtnStateKind::Basic, Some(0))
13412                .expect("state")
13413                .index(),
13414            2
13415        );
13416        assert_eq!(
13417            atn.add_state(AtnStateKind::Basic, Some(0))
13418                .expect("state")
13419                .index(),
13420            3
13421        );
13422        assert_eq!(
13423            atn.add_state(AtnStateKind::Basic, Some(0))
13424                .expect("state")
13425                .index(),
13426            4
13427        );
13428        assert_eq!(
13429            atn.add_state(AtnStateKind::RuleStop, Some(0))
13430                .expect("state")
13431                .index(),
13432            5
13433        );
13434        atn.set_rule_to_start_state(vec![0])
13435            .expect("rule start states");
13436        atn.set_rule_to_stop_state(vec![5])
13437            .expect("rule stop states");
13438        atn.add_decision_state(1).expect("decision state");
13439        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13440            .expect("transition");
13441        // Optional block: match A then fall through, or skip straight to state 3.
13442        atn.add_transition(
13443            1,
13444            ParserTransitionSpec::Atom {
13445                target: 3,
13446                label: 1,
13447            },
13448        )
13449        .expect("transition");
13450        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
13451            .expect("transition");
13452        // Match B, then EOF.
13453        atn.add_transition(
13454            3,
13455            ParserTransitionSpec::Atom {
13456                target: 4,
13457                label: 2,
13458            },
13459        )
13460        .expect("transition");
13461        atn.add_transition(
13462            4,
13463            ParserTransitionSpec::Atom {
13464                target: 5,
13465                label: TOKEN_EOF,
13466            },
13467        )
13468        .expect("transition");
13469        finish_atn(atn)
13470    }
13471
13472    #[test]
13473    fn sync_decision_deletes_only_a_single_token() {
13474        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
13475        // expected. `(A)? B EOF` at the optional-block decision:
13476        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
13477        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
13478        //               without consuming and records the expected set for the
13479        //               subsequent mismatch (the parser must not over-consume both
13480        //               `C`s and accept the input).
13481        let atn = optional_then_b_eof_atn();
13482
13483        let mut single = mini_parser(vec![
13484            TestToken::new(3).with_text("c"),
13485            TestToken::new(2).with_text("b"),
13486            TestToken::eof("parser-test", 1, 2, 2),
13487        ]);
13488        single.rule_context_stack = vec![RuleContextFrame {
13489            rule_index: 0,
13490            invoking_state: 0,
13491        }];
13492        let children = single
13493            .sync_decision(&atn, 1, true, false)
13494            .expect("single extraneous token recovers");
13495        assert_eq!(children.len(), 1);
13496        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
13497        assert_eq!(single.number_of_syntax_errors(), 1);
13498        // Exactly one token consumed (the cursor now sits on `b`).
13499        assert_eq!(single.la(1), 2);
13500
13501        let mut double = mini_parser(vec![
13502            TestToken::new(3).with_text("c"),
13503            TestToken::new(3).with_text("c"),
13504            TestToken::new(2).with_text("b"),
13505            TestToken::eof("parser-test", 1, 3, 3),
13506        ]);
13507        double.rule_context_stack = vec![RuleContextFrame {
13508            rule_index: 0,
13509            invoking_state: 0,
13510        }];
13511        let result = double.sync_decision(&atn, 1, true, false);
13512        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
13513        // consume either `c`. It reports the mismatch at the first `c` (so the parser
13514        // does not over-consume both and accept the input). Nothing is consumed, so
13515        // the cursor still sits on the first `c` for rule-level recovery.
13516        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
13517        match error {
13518            AntlrError::ParserError { message, .. } => {
13519                assert!(message.starts_with("mismatched input"), "got: {message}");
13520            }
13521            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
13522        }
13523        assert_eq!(double.la(1), 3);
13524    }
13525
13526    /// The real serialized ATN that `antlr4-rust-gen` emits for
13527    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
13528    /// the loop is `EOF`. The loop decision is state 5.
13529    fn star_loop_then_eof_atn() -> Atn {
13530        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13531            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,
13532            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,
13533            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,
13534            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
13535        ]))
13536        .deserialize_parser()
13537        .expect("star-loop-then-EOF ATN should deserialize")
13538    }
13539
13540    /// ATN for `s : a+ Y ; a : X ;`.
13541    ///
13542    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
13543    /// `+` loop must not treat that zero-width child as a successful iteration
13544    /// and then re-enter the loop at the same token index.
13545    fn plus_loop_with_recovering_body_atn() -> Atn {
13546        let mut atn = ParserAtnBuilder::new(2);
13547        assert_eq!(
13548            atn.add_state(AtnStateKind::RuleStart, Some(0))
13549                .expect("state")
13550                .index(),
13551            0
13552        );
13553        assert_eq!(
13554            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
13555                .expect("state")
13556                .index(),
13557            1
13558        );
13559        assert_eq!(
13560            atn.add_state(AtnStateKind::Basic, Some(0))
13561                .expect("state")
13562                .index(),
13563            2
13564        );
13565        assert_eq!(
13566            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13567                .expect("state")
13568                .index(),
13569            3
13570        );
13571        assert_eq!(
13572            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
13573                .expect("state")
13574                .index(),
13575            4
13576        );
13577        assert_eq!(
13578            atn.add_state(AtnStateKind::LoopEnd, Some(0))
13579                .expect("state")
13580                .index(),
13581            5
13582        );
13583        assert_eq!(
13584            atn.add_state(AtnStateKind::RuleStop, Some(0))
13585                .expect("state")
13586                .index(),
13587            6
13588        );
13589        assert_eq!(
13590            atn.add_state(AtnStateKind::RuleStart, Some(1))
13591                .expect("state")
13592                .index(),
13593            7
13594        );
13595        assert_eq!(
13596            atn.add_state(AtnStateKind::Basic, Some(1))
13597                .expect("state")
13598                .index(),
13599            8
13600        );
13601        assert_eq!(
13602            atn.add_state(AtnStateKind::RuleStop, Some(1))
13603                .expect("state")
13604                .index(),
13605            9
13606        );
13607        atn.set_rule_to_start_state(vec![0, 7])
13608            .expect("rule start states");
13609        atn.set_rule_to_stop_state(vec![6, 9])
13610            .expect("rule stop states");
13611        atn.set_end_state(1, 3).expect("block end state");
13612        atn.set_loop_back_state(5, 4).expect("loop back state");
13613        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13614            .expect("transition");
13615        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13616            .expect("transition");
13617        atn.add_transition(
13618            2,
13619            ParserTransitionSpec::Rule {
13620                target: 7,
13621                rule_index: 1,
13622                follow_state: 3,
13623                precedence: 0,
13624            },
13625        )
13626        .expect("transition");
13627        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13628            .expect("transition");
13629        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
13630            .expect("transition");
13631        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13632            .expect("transition");
13633        atn.add_transition(
13634            5,
13635            ParserTransitionSpec::Atom {
13636                target: 6,
13637                label: 2,
13638            },
13639        )
13640        .expect("transition");
13641        atn.add_transition(
13642            7,
13643            ParserTransitionSpec::Atom {
13644                target: 8,
13645                label: 1,
13646            },
13647        )
13648        .expect("transition");
13649        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13650            .expect("transition");
13651        finish_atn(atn)
13652    }
13653
13654    #[test]
13655    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
13656        let atn = plus_loop_with_recovering_body_atn();
13657        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
13658
13659        let error = parser
13660            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
13661            .expect_err("EOF recovery should report a bounded mismatch");
13662
13663        let AntlrError::ParserError { message, .. } = error else {
13664            panic!("expected ParserError, got {error:?}");
13665        };
13666        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
13667        assert_eq!(parser.number_of_syntax_errors(), 1);
13668        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
13669    }
13670
13671    #[test]
13672    fn sync_decision_deletes_token_before_eof_at_loop_back() {
13673        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
13674        // At the loop ENTRY (loop_back = false) a single unexpected token before
13675        // EOF is deleted as an error node (then the generated EOF match consumes
13676        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
13677        // EOF must be a valid scan-stop for this to fire.
13678        let atn = star_loop_then_eof_atn();
13679        let mut parser = mini_parser(vec![
13680            TestToken::new(2).with_text("c"),
13681            TestToken::eof("parser-test", 1, 1, 1),
13682        ]);
13683        parser.rule_context_stack = vec![RuleContextFrame {
13684            rule_index: 0,
13685            invoking_state: 0,
13686        }];
13687        let children = parser
13688            .sync_decision(&atn, 5, true, false)
13689            .expect("single token before EOF recovers");
13690        assert_eq!(children.len(), 1);
13691        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
13692        assert_eq!(parser.number_of_syntax_errors(), 1);
13693        assert_eq!(
13694            parser.la(1),
13695            TOKEN_EOF,
13696            "EOF is left for the rule's EOF match"
13697        );
13698    }
13699
13700    #[test]
13701    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
13702        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
13703        // single-token deletion, which fails because LA(2) = `c` is not expected —
13704        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
13705        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
13706        let atn = star_loop_then_eof_atn();
13707        let mut parser = mini_parser(vec![
13708            TestToken::new(2).with_text("c"),
13709            TestToken::new(2).with_text("c"),
13710            TestToken::eof("parser-test", 1, 2, 2),
13711        ]);
13712        parser.rule_context_stack = vec![RuleContextFrame {
13713            rule_index: 0,
13714            invoking_state: 0,
13715        }];
13716        let error = parser
13717            .sync_decision(&atn, 5, true, false)
13718            .expect_err("two tokens at the loop entry must not be deleted");
13719        match error {
13720            AntlrError::ParserError { message, .. } => {
13721                assert!(message.starts_with("mismatched input"), "got: {message}");
13722            }
13723            other => panic!("expected mismatched-input ParserError, got {other:?}"),
13724        }
13725        assert_eq!(
13726            parser.la(1),
13727            2,
13728            "nothing consumed; cursor still on first `c`"
13729        );
13730    }
13731
13732    #[test]
13733    fn sync_decision_consumes_until_eof_at_loop_back() {
13734        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
13735        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
13736        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
13737        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
13738        // post-`a` state directly: `c c <EOF>` with loop_back = true.
13739        let atn = star_loop_then_eof_atn();
13740        let mut parser = mini_parser(vec![
13741            TestToken::new(2).with_text("c"),
13742            TestToken::new(2).with_text("c"),
13743            TestToken::eof("parser-test", 1, 2, 2),
13744        ]);
13745        parser.rule_context_stack = vec![RuleContextFrame {
13746            rule_index: 0,
13747            invoking_state: 0,
13748        }];
13749        let children = parser
13750            .sync_decision(&atn, 5, false, true)
13751            .expect("loop-back multi-token deletion recovers onto EOF");
13752        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
13753        assert!(
13754            children
13755                .iter()
13756                .all(|child| parser.node(*child).kind() == NodeKind::Error)
13757        );
13758        assert_eq!(parser.number_of_syntax_errors(), 1);
13759        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
13760    }
13761
13762    fn predicate_after_token_atn() -> Atn {
13763        let mut atn = ParserAtnBuilder::new(2);
13764        assert_eq!(
13765            atn.add_state(AtnStateKind::RuleStart, Some(0))
13766                .expect("state")
13767                .index(),
13768            0
13769        );
13770        assert_eq!(
13771            atn.add_state(AtnStateKind::Basic, Some(0))
13772                .expect("state")
13773                .index(),
13774            1
13775        );
13776        assert_eq!(
13777            atn.add_state(AtnStateKind::Basic, Some(0))
13778                .expect("state")
13779                .index(),
13780            2
13781        );
13782        assert_eq!(
13783            atn.add_state(AtnStateKind::Basic, Some(0))
13784                .expect("state")
13785                .index(),
13786            3
13787        );
13788        assert_eq!(
13789            atn.add_state(AtnStateKind::RuleStop, Some(0))
13790                .expect("state")
13791                .index(),
13792            4
13793        );
13794        atn.set_rule_to_start_state(vec![0])
13795            .expect("rule start states");
13796        atn.set_rule_to_stop_state(vec![4])
13797            .expect("rule stop states");
13798        atn.add_transition(
13799            0,
13800            ParserTransitionSpec::Atom {
13801                target: 1,
13802                label: 1,
13803            },
13804        )
13805        .expect("transition");
13806        atn.add_transition(
13807            1,
13808            ParserTransitionSpec::Predicate {
13809                target: 2,
13810                rule_index: 0,
13811                pred_index: 0,
13812                context_dependent: false,
13813            },
13814        )
13815        .expect("transition");
13816        atn.add_transition(
13817            2,
13818            ParserTransitionSpec::Atom {
13819                target: 3,
13820                label: 2,
13821            },
13822        )
13823        .expect("transition");
13824        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13825            .expect("transition");
13826        finish_atn(atn)
13827    }
13828
13829    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
13830        let mut atn = ParserAtnBuilder::new(1);
13831        for (state_number, kind) in [
13832            (0, AtnStateKind::RuleStart),
13833            (1, AtnStateKind::BlockStart),
13834            (2, AtnStateKind::Basic),
13835            (3, AtnStateKind::Basic),
13836            (4, AtnStateKind::Basic),
13837            (5, AtnStateKind::Basic),
13838            (6, AtnStateKind::BlockEnd),
13839            (7, AtnStateKind::RuleStop),
13840        ] {
13841            assert_eq!(
13842                atn.add_state(kind, Some(0)).expect("state").index(),
13843                state_number
13844            );
13845        }
13846        atn.set_rule_to_start_state(vec![0])
13847            .expect("rule start states");
13848        atn.set_rule_to_stop_state(vec![7])
13849            .expect("rule stop states");
13850        atn.add_decision_state(1).expect("decision state");
13851        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13852            .expect("transition");
13853        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13854            .expect("transition");
13855        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
13856            .expect("transition");
13857        atn.add_transition(
13858            2,
13859            ParserTransitionSpec::Predicate {
13860                target: 4,
13861                rule_index: 0,
13862                pred_index: pred_indexes[0],
13863                context_dependent: false,
13864            },
13865        )
13866        .expect("transition");
13867        atn.add_transition(
13868            3,
13869            ParserTransitionSpec::Predicate {
13870                target: 5,
13871                rule_index: 0,
13872                pred_index: pred_indexes[1],
13873                context_dependent: false,
13874            },
13875        )
13876        .expect("transition");
13877        atn.add_transition(
13878            4,
13879            ParserTransitionSpec::Atom {
13880                target: 6,
13881                label: 1,
13882            },
13883        )
13884        .expect("transition");
13885        atn.add_transition(
13886            5,
13887            ParserTransitionSpec::Atom {
13888                target: 6,
13889                label: 1,
13890            },
13891        )
13892        .expect("transition");
13893        atn.add_transition(
13894            6,
13895            ParserTransitionSpec::Atom {
13896                target: 7,
13897                label: TOKEN_EOF,
13898            },
13899        )
13900        .expect("transition");
13901        finish_atn(atn)
13902    }
13903
13904    fn nested_nullable_context_atn() -> Atn {
13905        let mut atn = ParserAtnBuilder::new(1);
13906        for state_number in 0..=20 {
13907            let kind = match state_number {
13908                0 | 10 | 16 => AtnStateKind::RuleStart,
13909                9 | 15 | 20 => AtnStateKind::RuleStop,
13910                _ => AtnStateKind::Basic,
13911            };
13912            let rule_index = match state_number {
13913                0..=9 => 0,
13914                10..=15 => 1,
13915                _ => 2,
13916            };
13917            assert_eq!(
13918                atn.add_state(kind, Some(rule_index))
13919                    .expect("state")
13920                    .index(),
13921                state_number
13922            );
13923        }
13924        atn.set_rule_to_start_state(vec![0, 10, 16])
13925            .expect("rule start states");
13926        atn.set_rule_to_stop_state(vec![9, 15, 20])
13927            .expect("rule stop states");
13928        atn.add_transition(
13929            1,
13930            ParserTransitionSpec::Rule {
13931                target: 10,
13932                rule_index: 1,
13933                follow_state: 8,
13934                precedence: 0,
13935            },
13936        )
13937        .expect("transition");
13938        atn.add_transition(
13939            8,
13940            ParserTransitionSpec::Atom {
13941                target: 9,
13942                label: 1,
13943            },
13944        )
13945        .expect("transition");
13946        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13947            .expect("transition");
13948        atn.add_transition(
13949            2,
13950            ParserTransitionSpec::Rule {
13951                target: 16,
13952                rule_index: 2,
13953                follow_state: 14,
13954                precedence: 0,
13955            },
13956        )
13957        .expect("transition");
13958        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
13959            .expect("transition");
13960        finish_atn(atn)
13961    }
13962
13963    fn generated_match_recovery_atn() -> Atn {
13964        let mut atn = ParserAtnBuilder::new(2);
13965        assert_eq!(
13966            atn.add_state(AtnStateKind::RuleStart, Some(0))
13967                .expect("state")
13968                .index(),
13969            0
13970        );
13971        assert_eq!(
13972            atn.add_state(AtnStateKind::Basic, Some(0))
13973                .expect("state")
13974                .index(),
13975            1
13976        );
13977        assert_eq!(
13978            atn.add_state(AtnStateKind::Basic, Some(0))
13979                .expect("state")
13980                .index(),
13981            2
13982        );
13983        assert_eq!(
13984            atn.add_state(AtnStateKind::RuleStop, Some(0))
13985                .expect("state")
13986                .index(),
13987            3
13988        );
13989        assert_eq!(
13990            atn.add_state(AtnStateKind::RuleStart, Some(1))
13991                .expect("state")
13992                .index(),
13993            4
13994        );
13995        assert_eq!(
13996            atn.add_state(AtnStateKind::RuleStop, Some(1))
13997                .expect("state")
13998                .index(),
13999            5
14000        );
14001        atn.set_rule_to_start_state(vec![0, 4])
14002            .expect("rule start states");
14003        atn.set_rule_to_stop_state(vec![3, 5])
14004            .expect("rule stop states");
14005        atn.add_transition(
14006            1,
14007            ParserTransitionSpec::Rule {
14008                target: 4,
14009                rule_index: 1,
14010                follow_state: 2,
14011                precedence: 0,
14012            },
14013        )
14014        .expect("transition");
14015        atn.add_transition(
14016            2,
14017            ParserTransitionSpec::Atom {
14018                target: 3,
14019                label: TOKEN_EOF,
14020            },
14021        )
14022        .expect("transition");
14023        finish_atn(atn)
14024    }
14025
14026    fn complement_set_atn() -> Atn {
14027        let mut atn = ParserAtnBuilder::new(1);
14028        assert_eq!(
14029            atn.add_state(AtnStateKind::RuleStart, Some(0))
14030                .expect("state")
14031                .index(),
14032            0
14033        );
14034        assert_eq!(
14035            atn.add_state(AtnStateKind::RuleStop, Some(0))
14036                .expect("state")
14037                .index(),
14038            1
14039        );
14040        atn.set_rule_to_start_state(vec![0])
14041            .expect("rule start states");
14042        atn.set_rule_to_stop_state(vec![1])
14043            .expect("rule stop states");
14044        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
14045        atn.add_transition(
14046            0,
14047            ParserTransitionSpec::NotSet {
14048                target: 1,
14049                set: excluded,
14050            },
14051        )
14052        .expect("transition");
14053        finish_atn(atn)
14054    }
14055
14056    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
14057    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
14058    fn wildcard_then_eof_atn() -> Atn {
14059        let mut atn = ParserAtnBuilder::new(1);
14060        assert_eq!(
14061            atn.add_state(AtnStateKind::RuleStart, Some(0))
14062                .expect("state")
14063                .index(),
14064            0
14065        );
14066        assert_eq!(
14067            atn.add_state(AtnStateKind::RuleStop, Some(0))
14068                .expect("state")
14069                .index(),
14070            1
14071        );
14072        assert_eq!(
14073            atn.add_state(AtnStateKind::Basic, Some(0))
14074                .expect("state")
14075                .index(),
14076            2
14077        );
14078        atn.set_rule_to_start_state(vec![0])
14079            .expect("rule start states");
14080        atn.set_rule_to_stop_state(vec![1])
14081            .expect("rule stop states");
14082        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
14083            .expect("transition");
14084        atn.add_transition(
14085            2,
14086            ParserTransitionSpec::Atom {
14087                target: 1,
14088                label: TOKEN_EOF,
14089            },
14090        )
14091        .expect("transition");
14092        finish_atn(atn)
14093    }
14094
14095    #[test]
14096    fn parser_matches_token_and_reports_mismatch() {
14097        let source = Source {
14098            tokens: vec![
14099                TestToken::new(1).with_text("x"),
14100                TestToken::eof("parser-test", 1, 1, 1),
14101            ],
14102            index: 0,
14103        };
14104        let data = RecognizerData::new(
14105            "Mini.g4",
14106            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14107        );
14108        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
14109        let matched = parser.match_token(1).expect("token 1 should match");
14110        assert_eq!(parser.node(matched).text(), "x");
14111        assert!(parser.match_token(1).is_err());
14112    }
14113
14114    #[test]
14115    fn parser_matches_token_sets() {
14116        let mut parser = mini_parser(vec![
14117            TestToken::new(1).with_text("x"),
14118            TestToken::eof("parser-test", 1, 1, 1),
14119        ]);
14120
14121        let matched = parser
14122            .match_set(&[(1, 1), (3, 4)])
14123            .expect("token set should match");
14124        assert_eq!(parser.node(matched).text(), "x");
14125        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
14126    }
14127
14128    #[test]
14129    fn generated_rule_api_tracks_state_and_precedence() {
14130        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14131
14132        let context = parser.enter_rule(7, 2);
14133        assert_eq!(context.rule_index(), 2);
14134        assert_eq!(parser.state(), 7);
14135        assert_eq!(
14136            parser.rule_context_stack,
14137            vec![RuleContextFrame {
14138                rule_index: 2,
14139                invoking_state: 7
14140            }]
14141        );
14142
14143        let recursive = parser.enter_recursion_rule(11, 3, 4);
14144        assert_eq!(recursive.rule_index(), 3);
14145        assert!(parser.precpred(4));
14146        assert!(parser.precpred(5));
14147        assert!(!parser.precpred(3));
14148
14149        let next = parser.push_new_recursion_context(13, 3);
14150        assert_eq!(next.invoking_state(), 13);
14151        parser.unroll_recursion_context();
14152        assert_eq!(parser.precedence_stack, vec![0]);
14153        assert_eq!(
14154            parser.rule_context_stack,
14155            vec![RuleContextFrame {
14156                rule_index: 2,
14157                invoking_state: 7
14158            }]
14159        );
14160
14161        parser.exit_rule();
14162        assert!(parser.rule_context_stack.is_empty());
14163    }
14164
14165    #[test]
14166    fn active_invocation_states_exclude_the_root_frame() {
14167        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14168
14169        let _root = parser.enter_rule(0, 0);
14170        assert!(parser.active_invocation_states().is_empty());
14171
14172        let marker = parser.push_invoking_state(6);
14173        let _child = parser.enter_rule(2, 1);
14174        parser.discard_invoking_state(marker);
14175        assert_eq!(parser.active_invocation_states(), [6]);
14176
14177        let marker = parser.push_invoking_state(13);
14178        let _grandchild = parser.enter_rule(4, 2);
14179        parser.discard_invoking_state(marker);
14180        assert_eq!(parser.active_invocation_states(), [13, 6]);
14181
14182        parser.exit_rule();
14183        parser.exit_rule();
14184        parser.exit_rule();
14185    }
14186
14187    #[test]
14188    fn parser_predicates_support_token_adjacency() {
14189        let mut parser = mini_parser(vec![
14190            TestToken::new(1).with_text("=").with_span(0, 0),
14191            TestToken::new(1).with_text(">").with_span(1, 1),
14192            TestToken::eof("parser-test", 2, 1, 2),
14193        ]);
14194        parser.consume();
14195        parser.consume();
14196
14197        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
14198
14199        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14200
14201        let mut parser = mini_parser(vec![
14202            TestToken::new(1).with_text("=").with_span(0, 0),
14203            TestToken::new(1)
14204                .with_text(" ")
14205                .with_channel(HIDDEN_CHANNEL)
14206                .with_span(1, 1),
14207            TestToken::new(1).with_text(">").with_span(2, 2),
14208            TestToken::eof("parser-test", 3, 1, 3),
14209        ]);
14210        parser.consume();
14211        parser.consume();
14212
14213        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14214    }
14215
14216    #[test]
14217    fn parser_predicates_support_context_child_text_checks() {
14218        let mut parser = mini_parser(vec![
14219            TestToken::new(1).with_text("var"),
14220            TestToken::eof("parser-test", 1, 1, 1),
14221        ]);
14222        let mut context = ParserRuleContext::new(1, 0);
14223        let mut child_context = ParserRuleContext::new(2, 0);
14224        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
14225        parser.tree.add_child(&mut child_context, terminal);
14226        let child = parser.rule_node(child_context);
14227        parser.tree.add_child(&mut context, child);
14228        let predicates = [(
14229            1,
14230            0,
14231            ParserPredicate::ContextChildRuleTextNotEquals {
14232                rule_index: 2,
14233                text: "var",
14234            },
14235        )];
14236
14237        assert!(
14238            !parser.parser_semantic_predicate_matches_with_context_and_local(
14239                &predicates,
14240                1,
14241                0,
14242                &context,
14243                0,
14244            )
14245        );
14246    }
14247
14248    #[test]
14249    fn context_expected_symbols_walks_nullable_parent_contexts() {
14250        let atn = nested_nullable_context_atn();
14251        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14252        parser.rule_context_stack = vec![
14253            RuleContextFrame {
14254                rule_index: 0,
14255                invoking_state: 0,
14256            },
14257            RuleContextFrame {
14258                rule_index: 1,
14259                invoking_state: 1,
14260            },
14261            RuleContextFrame {
14262                rule_index: 2,
14263                invoking_state: 2,
14264            },
14265        ];
14266
14267        let expected = parser.context_expected_symbols(&atn);
14268
14269        assert!(expected.contains(&1));
14270        assert!(expected.contains(&TOKEN_EOF));
14271    }
14272
14273    #[test]
14274    fn prediction_context_return_states_track_rule_stack_changes() {
14275        let atn = nested_nullable_context_atn();
14276        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14277        parser.rule_context_stack = vec![
14278            RuleContextFrame {
14279                rule_index: 0,
14280                invoking_state: 0,
14281            },
14282            RuleContextFrame {
14283                rule_index: 1,
14284                invoking_state: 1,
14285            },
14286            RuleContextFrame {
14287                rule_index: 2,
14288                invoking_state: 2,
14289            },
14290        ];
14291
14292        let initial_version = parser.rule_context_version();
14293        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14294        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14295        assert_eq!(first, second);
14296        assert_eq!(parser.rule_context_version(), initial_version);
14297
14298        parser.exit_rule();
14299        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14300        assert_ne!(first, after_pop);
14301        assert_ne!(parser.rule_context_version(), initial_version);
14302    }
14303
14304    #[test]
14305    fn generated_match_token_recovers_missing_token_from_context_follow() {
14306        let atn = generated_match_recovery_atn();
14307        let data = RecognizerData::new(
14308            "Mini.g4",
14309            Vocabulary::new(
14310                [None, Some("'X'"), Some("'Y'")],
14311                [None, Some("X"), Some("Y")],
14312                [None::<&str>, None, None],
14313            ),
14314        );
14315        let mut parser = BaseParser::new(
14316            CommonTokenStream::new(Source {
14317                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14318                index: 0,
14319            }),
14320            data,
14321        );
14322        parser.rule_context_stack = vec![
14323            RuleContextFrame {
14324                rule_index: 0,
14325                invoking_state: 0,
14326            },
14327            RuleContextFrame {
14328                rule_index: 1,
14329                invoking_state: 1,
14330            },
14331        ];
14332        assert_eq!(parser.number_of_syntax_errors(), 0);
14333
14334        let node = parser
14335            .match_token_recovering(2, 5, &atn)
14336            .expect("generated match should insert missing token");
14337
14338        assert_eq!(node.children().len(), 1);
14339        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
14340        assert_eq!(
14341            node.clone()
14342                .into_child_iter()
14343                .map(|child| parser.node(child).text())
14344                .collect::<Vec<_>>(),
14345            ["<missing 'Y'>"]
14346        );
14347        // Single-token insertion synthesizes a missing token and consumes nothing,
14348        // so no EOF terminal is consumed even though lookahead is EOF.
14349        assert!(!node.consumed_eof());
14350        assert_eq!(parser.la(1), TOKEN_EOF);
14351        assert_eq!(parser.number_of_syntax_errors(), 1);
14352        assert_eq!(
14353            parser.generated_parser_diagnostics,
14354            [ParserDiagnostic {
14355                line: 1,
14356                column: 3,
14357                message: "missing 'Y' at '<EOF>'".to_owned(),
14358            }]
14359        );
14360    }
14361
14362    #[test]
14363    fn generated_match_token_counts_single_token_deletion_recovery() {
14364        let atn = generated_match_recovery_atn();
14365        let data = RecognizerData::new(
14366            "Mini.g4",
14367            Vocabulary::new(
14368                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
14369                [None, Some("X"), Some("Y"), Some("Z")],
14370                [None::<&str>, None, None, None],
14371            ),
14372        );
14373        let mut parser = BaseParser::new(
14374            CommonTokenStream::new(Source {
14375                tokens: vec![
14376                    TestToken::new(3).with_text("z"),
14377                    TestToken::new(2).with_text("y"),
14378                    TestToken::eof("parser-test", 3, 1, 3),
14379                ],
14380                index: 0,
14381            }),
14382            data,
14383        );
14384
14385        let node = parser
14386            .match_token_recovering(2, 5, &atn)
14387            .expect("generated match should delete the extraneous token");
14388
14389        assert_eq!(node.children().len(), 2);
14390        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
14391        assert_eq!(parser.node(node.children()[0]).text(), "z");
14392        assert_eq!(parser.node(node.children()[1]).text(), "y");
14393        assert_eq!(
14394            node.into_child_iter()
14395                .map(|child| parser.node(child).text())
14396                .collect::<Vec<_>>(),
14397            ["z", "y"]
14398        );
14399        assert_eq!(parser.number_of_syntax_errors(), 1);
14400    }
14401
14402    #[test]
14403    fn generated_match_token_iterates_single_success_without_a_children_vec() {
14404        let atn = generated_match_recovery_atn();
14405        let data = RecognizerData::new(
14406            "Mini.g4",
14407            Vocabulary::new(
14408                [None, Some("'X'"), Some("'Y'")],
14409                [None, Some("X"), Some("Y")],
14410                [None::<&str>, None, None],
14411            ),
14412        );
14413        let mut parser = BaseParser::new(
14414            CommonTokenStream::new(Source {
14415                tokens: vec![
14416                    TestToken::new(2).with_text("y"),
14417                    TestToken::eof("parser-test", 1, 1, 1),
14418                ],
14419                index: 0,
14420            }),
14421            data,
14422        );
14423
14424        let node = parser
14425            .match_token_recovering(2, 5, &atn)
14426            .expect("generated match should consume the expected token");
14427
14428        assert_eq!(
14429            node.into_child_iter()
14430                .map(|child| parser.node(child).text())
14431                .collect::<Vec<_>>(),
14432            ["y"]
14433        );
14434        assert_eq!(parser.number_of_syntax_errors(), 0);
14435    }
14436
14437    #[test]
14438    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
14439        let atn = generated_match_recovery_atn();
14440        let data = RecognizerData::new(
14441            "Mini.g4",
14442            Vocabulary::new(
14443                [None, Some("'X'"), Some("'Y'")],
14444                [None, Some("X"), Some("Y")],
14445                [None::<&str>, None, None],
14446            ),
14447        );
14448        let mut parser = BaseParser::new(
14449            CommonTokenStream::new(Source {
14450                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14451                index: 0,
14452            }),
14453            data,
14454        );
14455        parser.rule_context_stack = vec![
14456            RuleContextFrame {
14457                rule_index: 0,
14458                invoking_state: 0,
14459            },
14460            RuleContextFrame {
14461                rule_index: 1,
14462                invoking_state: 1,
14463            },
14464        ];
14465        let marker = parser.generated_diagnostics_checkpoint();
14466
14467        let _ = parser
14468            .match_token_recovering(2, 5, &atn)
14469            .expect("generated match should insert missing token");
14470        assert_eq!(parser.number_of_syntax_errors(), 1);
14471
14472        parser.restore_generated_diagnostics(marker);
14473
14474        assert_eq!(parser.number_of_syntax_errors(), 0);
14475        assert!(parser.generated_parser_diagnostics.is_empty());
14476    }
14477
14478    #[test]
14479    fn generated_prediction_diagnostics_use_adaptive_context() {
14480        let atn = two_alt_decision_atn();
14481        let data = RecognizerData::new(
14482            "Mini.g4",
14483            Vocabulary::new(
14484                [None, Some("'x'"), Some("'y'")],
14485                [None, Some("X"), Some("Y")],
14486                [None::<&str>, None, None],
14487            ),
14488        )
14489        .with_rule_names(["s"]);
14490        let mut parser = BaseParser::new(
14491            CommonTokenStream::new(Source {
14492                tokens: vec![
14493                    TestToken::new(1)
14494                        .with_text("x")
14495                        .with_position(1, 0)
14496                        .with_span(0, 0),
14497                    TestToken::new(2)
14498                        .with_text("y")
14499                        .with_position(1, 2)
14500                        .with_span(1, 1),
14501                    TestToken::eof("parser-test", 2, 1, 3),
14502                ],
14503                index: 0,
14504            }),
14505            data,
14506        );
14507        parser.set_report_diagnostic_errors(true);
14508
14509        parser.record_generated_prediction_diagnostic(
14510            &atn,
14511            1,
14512            &ParserAtnPrediction {
14513                alt: 1,
14514                requires_full_context: true,
14515                has_semantic_context: false,
14516                diagnostic: Some(ParserAtnPredictionDiagnostic {
14517                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
14518                    start_index: 0,
14519                    sll_stop_index: 1,
14520                    ll_stop_index: 0,
14521                    conflicting_alts: vec![1, 2],
14522                    exact: false,
14523                }),
14524            },
14525        );
14526        // Ambiguities from the default LL prediction mode are non-exact, so —
14527        // matching Java's exactOnly DiagnosticErrorListener — only the
14528        // attempting-full-context line is reported. Exact-ambiguity mode
14529        // reports the ambiguity itself.
14530        parser.record_generated_prediction_diagnostic(
14531            &atn,
14532            1,
14533            &ParserAtnPrediction {
14534                alt: 1,
14535                requires_full_context: true,
14536                has_semantic_context: false,
14537                diagnostic: Some(ParserAtnPredictionDiagnostic {
14538                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
14539                    start_index: 0,
14540                    sll_stop_index: 1,
14541                    ll_stop_index: 1,
14542                    conflicting_alts: vec![1, 2],
14543                    exact: false,
14544                }),
14545            },
14546        );
14547
14548        assert_eq!(
14549            parser.generated_parser_diagnostics,
14550            [
14551                ParserDiagnostic {
14552                    line: 1,
14553                    column: 2,
14554                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
14555                },
14556                ParserDiagnostic {
14557                    line: 1,
14558                    column: 0,
14559                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
14560                },
14561                ParserDiagnostic {
14562                    line: 1,
14563                    column: 2,
14564                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
14565                },
14566            ]
14567        );
14568    }
14569
14570    #[test]
14571    fn generated_match_not_set_recovers_empty_complement_at_eof() {
14572        let atn = complement_set_atn();
14573        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14574        parser.rule_context_stack = vec![RuleContextFrame {
14575            rule_index: 0,
14576            invoking_state: 0,
14577        }];
14578
14579        let node = parser
14580            .match_not_set_recovering(&[(1, 1)], 1, 1, 1, &atn)
14581            .expect("empty complement should recover at EOF");
14582
14583        assert_eq!(node.children().len(), 1);
14584        // Recovery synthesizes a missing token without consuming EOF, so the
14585        // enclosing rule must not record EOF as its stop token.
14586        assert!(!node.consumed_eof());
14587        assert_eq!(parser.la(1), TOKEN_EOF);
14588        assert_eq!(
14589            parser.generated_parser_diagnostics,
14590            [ParserDiagnostic {
14591                line: 1,
14592                column: 1,
14593                message: "missing {} at '<EOF>'".to_owned(),
14594            }]
14595        );
14596    }
14597
14598    #[test]
14599    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
14600        // `start : . EOF ;` on empty input. The wildcard is modeled as an
14601        // empty-complement not-set; at EOF the follow state (the explicit EOF
14602        // match) expects EOF, so even in the start rule recovery must perform
14603        // single-token insertion (`<missing ...>`) rather than aborting — matching
14604        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
14605        let atn = wildcard_then_eof_atn();
14606        let data = RecognizerData::new(
14607            "Mini.g4",
14608            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14609        );
14610        let mut parser = BaseParser::new(
14611            CommonTokenStream::new(Source {
14612                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
14613                index: 0,
14614            }),
14615            data,
14616        );
14617        parser.rule_context_stack = vec![RuleContextFrame {
14618            rule_index: 0,
14619            invoking_state: 0,
14620        }];
14621
14622        let node = parser
14623            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
14624            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
14625
14626        // A single `<missing ...>` error node is inserted; EOF is not consumed.
14627        assert_eq!(node.children().len(), 1);
14628        assert!(!node.consumed_eof());
14629        assert!(
14630            parser
14631                .node(node.children()[0])
14632                .text()
14633                .starts_with("<missing")
14634        );
14635        assert_eq!(parser.la(1), TOKEN_EOF);
14636        assert_eq!(
14637            parser.generated_parser_diagnostics,
14638            [ParserDiagnostic {
14639                line: 1,
14640                column: 1,
14641                message: "missing 'x' at '<EOF>'".to_owned(),
14642            }]
14643        );
14644    }
14645
14646    #[test]
14647    fn generated_rule_recovery_consumes_to_parent_follow() {
14648        let atn = generated_match_recovery_atn();
14649        let data = RecognizerData::new(
14650            "Mini.g4",
14651            Vocabulary::new(
14652                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
14653                [None, Some("X"), Some("Y"), Some("Z")],
14654                [None::<&str>, None, None, None],
14655            ),
14656        );
14657        let mut parser = BaseParser::new(
14658            CommonTokenStream::new(Source {
14659                tokens: vec![
14660                    TestToken::new(3).with_text("z"),
14661                    TestToken::eof("parser-test", 1, 1, 1),
14662                ],
14663                index: 0,
14664            }),
14665            data,
14666        );
14667        let _parent = parser.enter_rule(0, 0);
14668        let marker = parser.push_invoking_state(1);
14669        let mut child = parser.enter_rule(4, 1);
14670        parser.discard_invoking_state(marker);
14671
14672        parser.recover_generated_rule(
14673            &mut child,
14674            &atn,
14675            AntlrError::ParserError {
14676                line: 1,
14677                column: 0,
14678                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
14679            },
14680        );
14681        let tree = parser.finish_rule(child, false);
14682
14683        assert_eq!(parser.la(1), TOKEN_EOF);
14684        assert_eq!(
14685            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
14686            "(a z)"
14687        );
14688        assert_eq!(parser.number_of_syntax_errors(), 1);
14689        assert_eq!(
14690            parser.generated_parser_diagnostics,
14691            [ParserDiagnostic {
14692                line: 1,
14693                column: 0,
14694                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
14695            }]
14696        );
14697        parser.exit_rule();
14698    }
14699
14700    #[test]
14701    fn greedy_ll1_alt_handles_nullable_loop_exit() {
14702        let mut body_symbols = TokenBitSet::default();
14703        body_symbols.insert(1);
14704        let entry = DecisionLookahead {
14705            transitions: vec![
14706                TransitionLookSet {
14707                    symbols: body_symbols,
14708                    nullable: false,
14709                },
14710                TransitionLookSet {
14711                    symbols: TokenBitSet::default(),
14712                    nullable: true,
14713                },
14714            ],
14715        };
14716
14717        assert_eq!(ll1_unique_alt(&entry, 2), None);
14718        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
14719        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
14720        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
14721    }
14722
14723    #[test]
14724    fn ordinary_repetition_builds_tree_in_input_order() {
14725        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
14726            let mut parser = mini_parser(repeated_x_tokens(3));
14727            let tree = parser
14728                .parse_atn_rule(&atn, 0)
14729                .expect("ordinary repetition should parse");
14730
14731            let root = parser
14732                .node(tree)
14733                .as_rule()
14734                .expect("entry result should be a rule");
14735            let body_rules = root.child_rules(1).collect::<Vec<_>>();
14736            assert_eq!(root.text(), "xxx<EOF>");
14737            assert_eq!(body_rules.len(), 3);
14738            assert_eq!(
14739                body_rules
14740                    .iter()
14741                    .map(|rule| rule.start_id().expect("body start").index())
14742                    .collect::<Vec<_>>(),
14743                [0, 1, 2]
14744            );
14745            assert_eq!(
14746                body_rules
14747                    .iter()
14748                    .map(|rule| rule.stop_id().expect("body stop").index())
14749                    .collect::<Vec<_>>(),
14750                [0, 1, 2]
14751            );
14752            assert_eq!(parser.number_of_syntax_errors(), 0);
14753        }
14754    }
14755
14756    #[test]
14757    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
14758        const DEPTH: usize = 20_000;
14759
14760        std::thread::Builder::new()
14761            .name("deferred-rule-materialization".to_owned())
14762            .stack_size(256 * 1024)
14763            .spawn(|| {
14764                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
14765                let mut root = FastDeferredNodeId::EMPTY;
14766                for depth in 0..DEPTH {
14767                    root = parser
14768                        .recognition_arena
14769                        .deferred_rule_node(FastDeferredRule {
14770                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
14771                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
14772                            start_index: 0,
14773                            stop_index: None,
14774                            deferred_children: root,
14775                            children: NodeSeqId::EMPTY,
14776                        });
14777                }
14778
14779                let mut children = parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
14780                for expected_rule in (0..DEPTH).rev() {
14781                    let mut nodes = parser.recognition_arena.iter(children);
14782                    let node = nodes.next().expect("nested rule node");
14783                    assert!(nodes.next().is_none(), "each rule has one child");
14784                    let ArenaRecognizedNode::Rule {
14785                        rule_index,
14786                        children: nested,
14787                        ..
14788                    } = parser.recognition_arena.node(node)
14789                    else {
14790                        panic!("expected nested rule");
14791                    };
14792                    assert_eq!(rule_index as usize, expected_rule);
14793                    children = nested;
14794                }
14795                assert!(children.is_empty());
14796            })
14797            .expect("small-stack thread should start")
14798            .join()
14799            .expect("deferred rules should materialize without recursion");
14800    }
14801
14802    #[test]
14803    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
14804        const REPETITIONS: usize = 64;
14805
14806        let atn = ambiguous_ordinary_star_loop_atn();
14807        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
14808        let tree = parser
14809            .parse_atn_rule(&atn, 0)
14810            .expect("ambiguous ordinary repetition should parse");
14811
14812        let root = parser
14813            .node(tree)
14814            .as_rule()
14815            .expect("entry result should be a rule");
14816        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
14817        assert_eq!(parser.input.index(), REPETITIONS);
14818        assert!(
14819            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
14820            "equivalent segmentations should keep deferred storage linear"
14821        );
14822        assert_eq!(parser.number_of_syntax_errors(), 0);
14823    }
14824
14825    #[test]
14826    fn long_ordinary_repetition_does_not_consume_native_stack() {
14827        const REPETITIONS: usize = 20_000;
14828
14829        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
14830            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
14831            parser.set_build_parse_trees(false);
14832            parser
14833                .parse_atn_rule(&atn, 0)
14834                .expect("long ordinary repetition should parse");
14835
14836            assert_eq!(parser.input.index(), REPETITIONS);
14837            assert_eq!(parser.number_of_syntax_errors(), 0);
14838        }
14839    }
14840
14841    #[test]
14842    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
14843        const REPETITIONS: usize = 2_000;
14844        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
14845
14846        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
14847            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
14848            let tree = parser
14849                .parse_atn_rule(&atn, 0)
14850                .expect("long rule repetition should parse");
14851
14852            let root = parser
14853                .node(tree)
14854                .as_rule()
14855                .expect("entry result should be a rule");
14856            assert_eq!(root.text(), expected_text);
14857            assert_eq!(root.child_rules(1).count(), REPETITIONS);
14858            let first_body = root.child_rules(1).next().expect("first body rule");
14859            let last_body = root.child_rules(1).next_back().expect("last body rule");
14860            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
14861            assert_eq!(
14862                last_body.stop_id().expect("last body stop").index(),
14863                REPETITIONS - 1
14864            );
14865
14866            let stats = parser.recognition_arena_stats();
14867            assert_eq!(
14868                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
14869                (REPETITIONS, REPETITIONS, 0)
14870            );
14871            assert_eq!(
14872                (stats.total_links, stats.live_links, stats.dead_links),
14873                (REPETITIONS, REPETITIONS, 0)
14874            );
14875            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
14876            assert_eq!(
14877                parser.recognition_arena.deferred_nodes.len(),
14878                REPETITIONS * 2 - 1
14879            );
14880            assert_eq!(parser.number_of_syntax_errors(), 0);
14881        }
14882    }
14883
14884    #[test]
14885    fn single_outcome_memo_probe_selects_sparse_or_promote_mode() {
14886        let key = |state_number| FastRecognizeKey {
14887            state_number,
14888            stop_state: 10,
14889            index: state_number,
14890            rule_start_index: 0,
14891            decision_start_index: None,
14892            precedence: 0,
14893            recovery_symbols_id: 0,
14894            recovery_state: None,
14895        };
14896
14897        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14898        for state_number in 0..(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT - 1) {
14899            assert!(sparse.should_memoize_single_outcome(&key(state_number)));
14900        }
14901        assert!(!sparse.should_memoize_single_outcome(&key(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT)));
14902        assert_eq!(
14903            sparse.single_outcome_memo_mode,
14904            SingleOutcomeMemoMode::Sparse
14905        );
14906
14907        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14908        let repeated = key(1);
14909        for _ in 0..=CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
14910            assert!(promote.should_memoize_single_outcome(&repeated));
14911        }
14912        assert_eq!(
14913            promote.single_outcome_memo_mode,
14914            SingleOutcomeMemoMode::Promote
14915        );
14916    }
14917
14918    #[test]
14919    fn clean_empty_multi_alt_outcomes_are_memoized() {
14920        let mut atn = ParserAtnBuilder::new(2);
14921        assert_eq!(
14922            atn.add_state(AtnStateKind::RuleStart, Some(0))
14923                .expect("state")
14924                .index(),
14925            0
14926        );
14927        assert_eq!(
14928            atn.add_state(AtnStateKind::BlockStart, Some(0))
14929                .expect("state")
14930                .index(),
14931            1
14932        );
14933        assert_eq!(
14934            atn.add_state(AtnStateKind::RuleStop, Some(0))
14935                .expect("state")
14936                .index(),
14937            2
14938        );
14939        atn.set_rule_to_start_state(vec![0])
14940            .expect("rule start states");
14941        atn.set_rule_to_stop_state(vec![2])
14942            .expect("rule stop states");
14943        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14944            .expect("transition");
14945        atn.add_transition(
14946            1,
14947            ParserTransitionSpec::Atom {
14948                target: 2,
14949                label: 1,
14950            },
14951        )
14952        .expect("transition");
14953        atn.add_transition(
14954            1,
14955            ParserTransitionSpec::Atom {
14956                target: 2,
14957                label: 2,
14958            },
14959        )
14960        .expect("transition");
14961        let atn = finish_atn(atn);
14962
14963        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
14964        parser.fast_recovery_enabled = false;
14965        let mut visiting = FxHashSet::default();
14966        let mut memo = FxHashMap::default();
14967        let mut expected = ExpectedTokens::default();
14968        let outcomes = parser.recognize_state_fast(
14969            &atn,
14970            FastRecognizeRequest {
14971                state_number: 1,
14972                stop_state: 2,
14973                index: 0,
14974                rule_start_index: 0,
14975                decision_start_index: None,
14976                precedence: 0,
14977                depth: 0,
14978                recovery_symbols: parser.empty_recovery_symbols(),
14979                recovery_state: None,
14980            },
14981            FastRecognizeScratch {
14982                predicate_context: None,
14983                visiting: &mut visiting,
14984                memo: &mut memo,
14985                expected: &mut expected,
14986            },
14987        );
14988
14989        assert!(outcomes.is_empty());
14990        assert_eq!(memo.len(), 1);
14991        assert!(memo.values().next().expect("memo entry").is_empty());
14992    }
14993
14994    #[test]
14995    fn wildcard_matches_non_eof_only() {
14996        let mut parser = mini_parser(vec![
14997            TestToken::new(1).with_text("x"),
14998            TestToken::eof("parser-test", 1, 1, 1),
14999        ]);
15000        let matched = parser.match_wildcard().expect("wildcard");
15001        assert_eq!(parser.node(matched).text(), "x");
15002        assert!(parser.match_wildcard().is_err());
15003    }
15004
15005    #[test]
15006    fn add_parse_child_records_match_even_without_tree_building() {
15007        // `sync_decision`'s "is the current context empty" flag must reflect real
15008        // matches, not parse-tree children: when `build_parse_trees(false)`,
15009        // `children` stays empty but `has_matched_child` must still flip so nested
15010        // recovery does not wrongly suppress single-token deletion.
15011        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15012        let token = TestToken::new(1).with_text("x");
15013
15014        parser.set_build_parse_trees(false);
15015        let mut ctx = ParserRuleContext::new(0, 0);
15016        assert!(!ctx.has_matched_child());
15017        let child = parser.terminal_tree(token.id);
15018        parser.add_parse_child(&mut ctx, child);
15019        // Tree building is off, so no child is stored...
15020        assert_eq!(ctx.child_count(), 0);
15021        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15022        // ...but the match is recorded, so the context is no longer "empty".
15023        assert!(ctx.has_matched_child());
15024
15025        // With tree building on, the child is stored and the match is recorded.
15026        parser.set_build_parse_trees(true);
15027        let mut ctx = ParserRuleContext::new(0, 0);
15028        let child = parser.terminal_tree(token.id);
15029        parser.add_parse_child(&mut ctx, child);
15030        assert_eq!(ctx.child_count(), 1);
15031        assert!(ctx.has_matched_child());
15032    }
15033
15034    #[test]
15035    fn disabled_tree_building_does_not_grow_flat_storage() {
15036        let mut parser = mini_parser(vec![
15037            TestToken::new(1).with_text("x"),
15038            TestToken::new(1).with_text("y"),
15039            TestToken::eof("parser-test", 2, 1, 2),
15040        ]);
15041        parser.set_build_parse_trees(false);
15042        let mut context = ParserRuleContext::new(0, -1);
15043
15044        for _ in 0..2 {
15045            let child = parser.match_token(1).expect("token should match");
15046            parser.add_parse_child(&mut context, child);
15047        }
15048        let current = parser.input.lt_id(1).expect("EOF token");
15049        let error = parser.error_tree(current);
15050        parser.add_parse_child(&mut context, error);
15051        let root = parser.rule_node(context);
15052
15053        assert_eq!(
15054            parser.parse_tree_storage().stats(),
15055            ParseTreeStats::default()
15056        );
15057        assert!(
15058            parser
15059                .parse_tree_storage()
15060                .node(parser.token_store(), root)
15061                .is_none(),
15062            "the no-tree sentinel must not resolve to stored data"
15063        );
15064    }
15065
15066    #[test]
15067    fn parser_interprets_simple_atn_rule() {
15068        let atn = token_then_eof_atn();
15069        let mut parser = mini_parser(vec![
15070            TestToken::new(1).with_text("x"),
15071            TestToken::eof("parser-test", 1, 1, 1),
15072        ]);
15073
15074        let tree = parser
15075            .parse_atn_rule(&atn, 0)
15076            .expect("artificial parser rule should parse");
15077        assert_eq!(parser.node(tree).text(), "x<EOF>");
15078        assert_eq!(parser.number_of_syntax_errors(), 0);
15079        assert_eq!(
15080            parser
15081                .node(tree)
15082                .first_rule_stop(0)
15083                .expect("rule should stop at EOF")
15084                .token_type(),
15085            TOKEN_EOF
15086        );
15087
15088        let mut parser = mini_parser(vec![
15089            TestToken::new(1).with_text("x"),
15090            TestToken::eof("parser-test", 1, 1, 1),
15091        ]);
15092        let (tree, actions) = parser
15093            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15094            .expect("runtime-option parser rule should parse");
15095        assert!(actions.is_empty());
15096        assert_eq!(
15097            parser
15098                .node(tree)
15099                .first_rule_stop(0)
15100                .expect("rule should stop at EOF")
15101                .token_type(),
15102            TOKEN_EOF
15103        );
15104    }
15105
15106    #[test]
15107    fn runtime_options_default_ignores_noop_action_transitions() {
15108        let atn = noop_action_then_token_then_eof_atn();
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 (tree, actions) = parser
15115            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15116            .expect("no-op parser action should not force action replay");
15117
15118        assert_eq!(parser.node(tree).text(), "x<EOF>");
15119        assert!(
15120            actions.is_empty(),
15121            "action_index=None transitions are ANTLR metadata, not replay actions"
15122        );
15123        assert_eq!(parser.number_of_syntax_errors(), 0);
15124    }
15125
15126    #[test]
15127    fn parser_exposes_buffered_token_stream_after_parse() {
15128        let atn = token_then_eof_atn();
15129        let mut parser = mini_parser(vec![
15130            TestToken::new(1).with_text("x"),
15131            TestToken::eof("parser-test", 1, 1, 1),
15132        ]);
15133
15134        let tree = parser
15135            .parse_atn_rule(&atn, 0)
15136            .expect("artificial parser rule should parse");
15137        assert_eq!(parser.node(tree).text(), "x<EOF>");
15138
15139        let stream = parser.token_stream();
15140        let source_index_after_parse = stream.token_source().index;
15141        let buffered = stream.tokens().collect::<Vec<_>>();
15142        assert_eq!(buffered.len(), 2);
15143        assert_eq!(buffered[0].text(), "x");
15144        assert_eq!(buffered[0].token_id().index(), 0);
15145        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
15146        assert_eq!(stream.token_source().index, source_index_after_parse);
15147        drop(buffered);
15148
15149        let stream = parser.into_token_stream();
15150        assert_eq!(stream.token_source().index, source_index_after_parse);
15151        assert_eq!(stream.tokens().next().expect("first token").text(), "x");
15152        assert_eq!(
15153            stream.tokens().nth(1).expect("EOF token").token_type(),
15154            TOKEN_EOF
15155        );
15156    }
15157
15158    #[test]
15159    fn parser_syntax_error_count_tracks_interpreted_recovery() {
15160        let atn = token_then_eof_atn();
15161        let mut parser = mini_parser(vec![
15162            TestToken::new(1).with_text("x"),
15163            TestToken::new(2).with_text("y"),
15164            TestToken::eof("parser-test", 2, 1, 2),
15165        ]);
15166
15167        let tree = parser
15168            .parse_atn_rule(&atn, 0)
15169            .expect("invalid token should recover into an error node");
15170
15171        assert_eq!(parser.number_of_syntax_errors(), 1);
15172        assert_eq!(
15173            parser
15174                .node(tree)
15175                .first_error_token()
15176                .expect("recovery should embed an error token")
15177                .text(),
15178            "y"
15179        );
15180    }
15181
15182    #[test]
15183    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
15184        let atn = token_then_eof_atn();
15185        let mut parser = mini_parser(vec![
15186            TestToken::new(2).with_text("y"),
15187            TestToken::eof("parser-test", 1, 1, 1),
15188        ]);
15189
15190        let error = parser
15191            .parse_atn_rule(&atn, 0)
15192            .expect_err("start-rule mismatch should remain a parser error");
15193
15194        assert_eq!(parser.number_of_syntax_errors(), 1);
15195        assert!(matches!(error, AntlrError::ParserError { .. }));
15196    }
15197
15198    #[test]
15199    fn adaptive_direct_rule_uses_simulator_decision() {
15200        let atn = two_alt_decision_atn();
15201        let mut simulator = ParserAtnSimulator::new(&atn);
15202        let mut parser = mini_parser(vec![
15203            TestToken::new(2).with_text("y"),
15204            TestToken::eof("parser-test", 1, 1, 1),
15205        ]);
15206
15207        let tree = parser
15208            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15209            .expect("direct adaptive rule should parse");
15210
15211        assert_eq!(parser.node(tree).text(), "y");
15212        assert_eq!(parser.input.index(), 1);
15213    }
15214
15215    #[test]
15216    fn adaptive_direct_rule_restores_input_on_fallback() {
15217        let atn = predicate_after_token_atn();
15218        let mut simulator = ParserAtnSimulator::new(&atn);
15219        let mut parser = mini_parser(vec![
15220            TestToken::new(1).with_text("x"),
15221            TestToken::new(2).with_text("y"),
15222            TestToken::eof("parser-test", 2, 1, 2),
15223        ]);
15224
15225        let tree = parser
15226            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15227            .expect("fallback recognizer should parse");
15228
15229        assert_eq!(parser.node(tree).text(), "xy");
15230        assert_eq!(parser.input.index(), 2);
15231        let stats = parser.parse_tree_storage().stats();
15232        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
15233        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
15234        assert_eq!(stats.scratch_links, 0);
15235    }
15236
15237    #[test]
15238    fn unknown_predicate_policy_defaults_to_assume_true() {
15239        let atn = predicate_after_token_atn();
15240        let mut parser = mini_parser(vec![
15241            TestToken::new(1).with_text("x"),
15242            TestToken::new(2).with_text("y"),
15243            TestToken::eof("parser-test", 2, 1, 2),
15244        ]);
15245
15246        let (tree, _) = parser
15247            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15248            .expect("unknown predicate should pass under the default policy");
15249
15250        assert_eq!(parser.node(tree).text(), "xy");
15251        assert_eq!(parser.number_of_syntax_errors(), 0);
15252    }
15253
15254    #[test]
15255    fn predicate_gated_same_lookahead_uses_viable_alternative() {
15256        let atn = predicate_gated_same_lookahead_atn([0, 1]);
15257        let mut parser = mini_parser(vec![
15258            TestToken::new(1).with_text("x"),
15259            TestToken::eof("parser-test", 1, 1, 1),
15260        ]);
15261
15262        let (tree, _) = parser
15263            .parse_atn_rule_with_runtime_options(
15264                &atn,
15265                0,
15266                ParserRuntimeOptions {
15267                    predicates: &[
15268                        (0, 0, ParserPredicate::False),
15269                        (0, 1, ParserPredicate::True),
15270                    ],
15271                    ..ParserRuntimeOptions::default()
15272                },
15273            )
15274            .expect("the second predicate-gated alternative should match");
15275
15276        assert_eq!(parser.node(tree).text(), "x<EOF>");
15277        assert_eq!(parser.number_of_syntax_errors(), 0);
15278        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
15279        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
15280    }
15281
15282    #[test]
15283    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
15284        // A generated parent may record an unknown-predicate coordinate, then
15285        // descend into an interpreted child. The child's interpreter entry must
15286        // not wipe the parent's recorded hit before the top-level surfaces it.
15287        let atn = token_then_eof_atn();
15288        let mut parser = mini_parser(vec![
15289            TestToken::new(1).with_text("x"),
15290            TestToken::eof("parser-test", 1, 1, 1),
15291        ]);
15292
15293        // Simulate the parent having recorded a fail-loud coordinate.
15294        parser.unknown_predicate_hits.push((7, 3));
15295
15296        // Run an interpreted child parse that records no coordinate of its own.
15297        parser
15298            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15299            .expect("child rule parses");
15300
15301        // The parent's coordinate must still be present for the top-level entry.
15302        let error = parser
15303            .take_unknown_semantic_error()
15304            .expect("parent's recorded coordinate must survive the nested interpreted parse");
15305        let AntlrError::Unsupported(message) = error else {
15306            panic!("expected AntlrError::Unsupported, got {error:?}");
15307        };
15308        assert!(message.contains("pred_index=3"), "message: {message}");
15309    }
15310
15311    #[test]
15312    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
15313        let atn = predicate_after_token_atn();
15314        let mut parser = mini_parser(vec![
15315            TestToken::new(1).with_text("x"),
15316            TestToken::new(2).with_text("y"),
15317            TestToken::eof("parser-test", 2, 1, 2),
15318        ]);
15319
15320        let result = parser.parse_atn_rule_with_runtime_options(
15321            &atn,
15322            0,
15323            ParserRuntimeOptions {
15324                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
15325                ..ParserRuntimeOptions::default()
15326            },
15327        );
15328
15329        assert!(
15330            result.is_err(),
15331            "the only path is predicate-guarded, so assume-false must fail the parse"
15332        );
15333    }
15334
15335    #[test]
15336    fn predicate_failure_message_keeps_semantic_recovery_path() {
15337        let atn = predicate_after_token_atn();
15338        let mut parser = mini_parser(vec![
15339            TestToken::new(1).with_text("x"),
15340            TestToken::new(2).with_text("y"),
15341            TestToken::eof("parser-test", 2, 1, 2),
15342        ]);
15343
15344        let (tree, _) = parser
15345            .parse_atn_rule_with_runtime_options(
15346                &atn,
15347                0,
15348                ParserRuntimeOptions {
15349                    predicates: &[(
15350                        0,
15351                        0,
15352                        ParserPredicate::FalseWithMessage {
15353                            message: "predicate rejected input",
15354                        },
15355                    )],
15356                    ..ParserRuntimeOptions::default()
15357                },
15358            )
15359            .expect("failure-message predicates recover through the semantic interpreter");
15360
15361        assert_eq!(parser.node(tree).text(), "xy");
15362        assert_eq!(parser.number_of_syntax_errors(), 1);
15363        assert!(
15364            parser.fast_predicate_cache.is_empty(),
15365            "failure-message predicates need the semantic interpreter's recovery outcome"
15366        );
15367    }
15368
15369    #[test]
15370    fn unknown_predicate_policy_error_names_the_coordinate() {
15371        let atn = predicate_after_token_atn();
15372        let mut parser = mini_parser(vec![
15373            TestToken::new(1).with_text("x"),
15374            TestToken::new(2).with_text("y"),
15375            TestToken::eof("parser-test", 2, 1, 2),
15376        ]);
15377
15378        let error = parser
15379            .parse_atn_rule_with_runtime_options(
15380                &atn,
15381                0,
15382                ParserRuntimeOptions {
15383                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15384                    ..ParserRuntimeOptions::default()
15385                },
15386            )
15387            .expect_err("evaluating an unknown predicate under Error policy must fail");
15388
15389        let AntlrError::Unsupported(message) = error else {
15390            panic!("expected AntlrError::Unsupported, got {error:?}");
15391        };
15392        assert!(
15393            message.contains("unsupported semantic predicate"),
15394            "message should name the failure class: {message}"
15395        );
15396        assert!(
15397            message.contains("pred_index=0"),
15398            "message should carry the coordinate: {message}"
15399        );
15400    }
15401
15402    #[test]
15403    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
15404        // A parser reused after a fail-loud parse must not carry the old
15405        // coordinates into a later parse. The fail-loud return keeps the hits
15406        // (so a generated parent can surface a recovered child's coordinate),
15407        // and the next parse's entry stashes/replaces them, so a subsequent
15408        // clean parse surfaces no stale error.
15409        let atn = predicate_after_token_atn();
15410        let mut parser = mini_parser(vec![
15411            TestToken::new(1).with_text("x"),
15412            TestToken::new(2).with_text("y"),
15413            TestToken::eof("parser-test", 2, 1, 2),
15414        ]);
15415
15416        parser
15417            .parse_atn_rule_with_runtime_options(
15418                &atn,
15419                0,
15420                ParserRuntimeOptions {
15421                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15422                    ..ParserRuntimeOptions::default()
15423                },
15424            )
15425            .expect_err("first parse fails loud under the Error policy");
15426
15427        // The failed parse kept its coordinate on the parser (so a generated
15428        // parent could surface a recovered child). A top-level reuse resets the
15429        // hits — generated parsers call `reset_unknown_semantic_hits` at their
15430        // public entry; direct interpreter-API callers do the same.
15431        parser.reset_unknown_semantic_hits();
15432        assert!(
15433            parser.take_unknown_semantic_error().is_none(),
15434            "reset must drop stale unknown-predicate coordinates before a reused parse"
15435        );
15436    }
15437
15438    #[derive(Debug, Default)]
15439    struct RecordingHooks {
15440        predicates: Vec<(usize, usize, usize, Option<String>)>,
15441        actions: Vec<(usize, String, Option<String>)>,
15442        action_trees: Vec<Option<String>>,
15443    }
15444
15445    impl SemanticHooks for RecordingHooks {
15446        fn sempred<S>(
15447            &mut self,
15448            ctx: &mut ParserSemCtx<'_, S>,
15449            rule_index: usize,
15450            pred_index: usize,
15451        ) -> Option<bool>
15452        where
15453            S: TokenSource,
15454        {
15455            self.predicates.push((
15456                ctx.input_index(),
15457                rule_index,
15458                pred_index,
15459                ctx.token_text(1).map(|token| token.text().to_owned()),
15460            ));
15461            Some(true)
15462        }
15463
15464        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
15465        where
15466            S: TokenSource,
15467        {
15468            self.actions.push((
15469                action.source_state(),
15470                ctx.action_text(),
15471                ctx.rule_name().map(str::to_owned),
15472            ));
15473            self.action_trees.push(ctx.tree().map(Node::text));
15474            true
15475        }
15476    }
15477
15478    #[derive(Debug, Default)]
15479    struct RejectingPredicateHooks {
15480        predicates: Vec<(usize, usize, usize, Option<String>)>,
15481    }
15482
15483    impl SemanticHooks for RejectingPredicateHooks {
15484        fn sempred<S>(
15485            &mut self,
15486            ctx: &mut ParserSemCtx<'_, S>,
15487            rule_index: usize,
15488            pred_index: usize,
15489        ) -> Option<bool>
15490        where
15491            S: TokenSource,
15492        {
15493            self.predicates.push((
15494                ctx.input_index(),
15495                rule_index,
15496                pred_index,
15497                ctx.token_text(1).map(|token| token.text().to_owned()),
15498            ));
15499            Some(false)
15500        }
15501    }
15502
15503    #[test]
15504    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
15505        let atn = predicate_gated_same_lookahead_atn([0, 0]);
15506        let mut parser = mini_parser_with_hooks(
15507            vec![
15508                TestToken::new(1).with_text("x"),
15509                TestToken::eof("parser-test", 1, 1, 1),
15510            ],
15511            RecordingHooks::default(),
15512        );
15513
15514        let (tree, _) = parser
15515            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15516            .expect("both alternatives share one replay-safe predicate result");
15517
15518        assert_eq!(parser.node(tree).text(), "x<EOF>");
15519        assert_eq!(
15520            parser.semantic_hooks.predicates,
15521            vec![(0, 0, 0, Some("x".to_owned()))]
15522        );
15523        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
15524    }
15525
15526    #[test]
15527    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
15528        let atn = predicate_after_token_atn();
15529        let mut parser = mini_parser_with_hooks(
15530            vec![
15531                TestToken::new(1).with_text("x"),
15532                TestToken::new(2).with_text("y"),
15533                TestToken::eof("parser-test", 2, 1, 2),
15534            ],
15535            RecordingHooks::default(),
15536        );
15537
15538        let (tree, _) = parser
15539            .parse_atn_rule_with_runtime_options(
15540                &atn,
15541                0,
15542                ParserRuntimeOptions {
15543                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15544                    ..ParserRuntimeOptions::default()
15545                },
15546            )
15547            .expect("hook supplies the missing predicate result");
15548
15549        assert_eq!(parser.node(tree).text(), "xy");
15550        assert_eq!(
15551            parser.semantic_hooks.predicates,
15552            vec![(1, 0, 0, Some("y".to_owned()))]
15553        );
15554        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
15555    }
15556
15557    #[test]
15558    fn runtime_options_default_preserves_semantic_hook_predicates() {
15559        let atn = predicate_after_token_atn();
15560        let mut parser = mini_parser_with_hooks(
15561            vec![
15562                TestToken::new(1).with_text("x"),
15563                TestToken::new(2).with_text("y"),
15564                TestToken::eof("parser-test", 2, 1, 2),
15565            ],
15566            RejectingPredicateHooks::default(),
15567        );
15568
15569        let result =
15570            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
15571
15572        assert!(
15573            result.is_err(),
15574            "default runtime options must not bypass semantic hooks for predicate ATNs"
15575        );
15576        assert_eq!(
15577            parser.semantic_hooks.predicates,
15578            vec![(1, 0, 0, Some("y".to_owned()))]
15579        );
15580        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
15581    }
15582
15583    #[test]
15584    fn semantic_hook_handles_committed_parser_action() {
15585        let atn = token_then_eof_atn();
15586        let mut parser = mini_parser_with_hooks(
15587            vec![
15588                TestToken::new(1).with_text("x"),
15589                TestToken::eof("parser-test", 1, 1, 1),
15590            ],
15591            RecordingHooks::default(),
15592        );
15593        let (tree, _) = parser
15594            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15595            .expect("rule parses before action hook is tested");
15596
15597        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
15598        assert_eq!(
15599            parser.semantic_hooks.actions,
15600            vec![(42, "x".to_owned(), Some("s".to_owned()))]
15601        );
15602        assert_eq!(
15603            parser.semantic_hooks.action_trees,
15604            [Some("x<EOF>".to_owned())]
15605        );
15606    }
15607
15608    #[test]
15609    fn unhandled_committed_action_fails_loud_under_error_policy() {
15610        // An action offered to the hook that no hook handles (returns false)
15611        // must be recorded and surfaced as `AntlrError::Unsupported` under the
15612        // Error policy, so a `hook`-disposed action is not silently dropped.
15613        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
15614        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
15615        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
15616
15617        // DecliningHooks::action returns false (unhandled).
15618        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
15619
15620        let error = parser
15621            .take_unknown_semantic_error()
15622            .expect("an unhandled committed action under Error policy must fail loud");
15623        let AntlrError::Unsupported(message) = error else {
15624            panic!("expected AntlrError::Unsupported, got {error:?}");
15625        };
15626        assert!(
15627            message.contains("unhandled semantic action") && message.contains("state=42"),
15628            "message should name the dropped action coordinate: {message}"
15629        );
15630
15631        // Under the default (assume-true) policy the same miss is not recorded.
15632        let mut lenient =
15633            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
15634        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
15635        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
15636        assert!(lenient.take_unknown_semantic_error().is_none());
15637    }
15638
15639    #[test]
15640    fn translated_predicate_is_unaffected_by_error_policy() {
15641        let atn = predicate_after_token_atn();
15642        let mut parser = mini_parser(vec![
15643            TestToken::new(1).with_text("x"),
15644            TestToken::new(2).with_text("y"),
15645            TestToken::eof("parser-test", 2, 1, 2),
15646        ]);
15647
15648        let (tree, _) = parser
15649            .parse_atn_rule_with_runtime_options(
15650                &atn,
15651                0,
15652                ParserRuntimeOptions {
15653                    predicates: &[(0, 0, ParserPredicate::True)],
15654                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15655                    ..ParserRuntimeOptions::default()
15656                },
15657            )
15658            .expect("a predicate covered by the table is not an unknown coordinate");
15659
15660        assert_eq!(parser.node(tree).text(), "xy");
15661    }
15662
15663    /// Hooks that decline (`None`) must fall through to the configured policy
15664    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
15665    /// legacy table path. Regression for the `unwrap_or(false)` that silently
15666    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
15667    fn hook_predicate_semantics() -> ParserSemantics {
15668        let mut ir = SemIr::new();
15669        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
15670        ParserSemantics {
15671            ir,
15672            predicates: vec![ParserSemanticPredicate {
15673                rule_index: 0,
15674                pred_index: 0,
15675                expr,
15676                failure_message: None,
15677            }],
15678            actions: Vec::new(),
15679        }
15680    }
15681
15682    #[derive(Debug, Default)]
15683    struct DecliningHooks;
15684
15685    impl SemanticHooks for DecliningHooks {}
15686
15687    #[test]
15688    fn semir_hook_none_falls_through_to_assume_true() {
15689        let atn = predicate_after_token_atn();
15690        let semantics = hook_predicate_semantics();
15691        let mut parser = mini_parser_with_hooks(
15692            vec![
15693                TestToken::new(1).with_text("x"),
15694                TestToken::new(2).with_text("y"),
15695                TestToken::eof("parser-test", 2, 1, 2),
15696            ],
15697            DecliningHooks,
15698        );
15699
15700        let (tree, _) = parser
15701            .parse_atn_rule_with_runtime_options(
15702                &atn,
15703                0,
15704                ParserRuntimeOptions {
15705                    semantics: Some(&semantics),
15706                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
15707                    ..ParserRuntimeOptions::default()
15708                },
15709            )
15710            .expect("a declined SemIR hook must pass under assume-true");
15711
15712        assert_eq!(parser.node(tree).text(), "xy");
15713    }
15714
15715    #[test]
15716    fn semir_hook_none_falls_through_to_assume_false() {
15717        let atn = predicate_after_token_atn();
15718        let semantics = hook_predicate_semantics();
15719        let mut parser = mini_parser_with_hooks(
15720            vec![
15721                TestToken::new(1).with_text("x"),
15722                TestToken::new(2).with_text("y"),
15723                TestToken::eof("parser-test", 2, 1, 2),
15724            ],
15725            DecliningHooks,
15726        );
15727
15728        let result = parser.parse_atn_rule_with_runtime_options(
15729            &atn,
15730            0,
15731            ParserRuntimeOptions {
15732                semantics: Some(&semantics),
15733                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
15734                ..ParserRuntimeOptions::default()
15735            },
15736        );
15737
15738        assert!(
15739            result.is_err(),
15740            "a declined SemIR hook must fail the only guarded path under assume-false"
15741        );
15742    }
15743
15744    #[test]
15745    fn semir_hook_none_records_coordinate_under_error_policy() {
15746        let atn = predicate_after_token_atn();
15747        let semantics = hook_predicate_semantics();
15748        let mut parser = mini_parser_with_hooks(
15749            vec![
15750                TestToken::new(1).with_text("x"),
15751                TestToken::new(2).with_text("y"),
15752                TestToken::eof("parser-test", 2, 1, 2),
15753            ],
15754            DecliningHooks,
15755        );
15756
15757        let error = parser
15758            .parse_atn_rule_with_runtime_options(
15759                &atn,
15760                0,
15761                ParserRuntimeOptions {
15762                    semantics: Some(&semantics),
15763                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15764                    ..ParserRuntimeOptions::default()
15765                },
15766            )
15767            .expect_err("a declined SemIR hook under Error policy must fail the parse");
15768
15769        let AntlrError::Unsupported(message) = error else {
15770            panic!("expected AntlrError::Unsupported, got {error:?}");
15771        };
15772        assert!(
15773            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
15774            "message should name the unresolved coordinate: {message}"
15775        );
15776    }
15777
15778    #[test]
15779    fn generated_direct_predicate_honors_installed_policy() {
15780        // The generated recursive-descent path calls
15781        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
15782        // going through `ParserRuntimeOptions`, so the policy must be installed
15783        // via `set_unknown_predicate_policy` (as the generated constructor now
15784        // does). A declining hook must then honor it rather than the default.
15785        let semantics = hook_predicate_semantics();
15786        let context = ParserRuleContext::new(0, -1);
15787
15788        let mut assume_true =
15789            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
15790        assert!(
15791            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
15792                &semantics, 0, 0, &context, 0
15793            ),
15794            "default AssumeTrue accepts a declined hook"
15795        );
15796        assert!(assume_true.take_unknown_semantic_error().is_none());
15797
15798        let mut error_policy =
15799            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
15800        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
15801        assert!(
15802            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
15803                &semantics, 0, 0, &context, 0
15804            ),
15805            "Error policy rejects a declined hook on the generated-direct path"
15806        );
15807        let error = error_policy
15808            .take_unknown_semantic_error()
15809            .expect("Error policy records the unresolved coordinate for the generated path");
15810        let AntlrError::Unsupported(message) = error else {
15811            panic!("expected AntlrError::Unsupported, got {error:?}");
15812        };
15813        assert!(message.contains("pred_index=0"), "message: {message}");
15814    }
15815
15816    #[test]
15817    fn parser_rule_start_skips_leading_hidden_tokens() {
15818        let atn = token_then_eof_atn();
15819        let mut parser = mini_parser(vec![
15820            TestToken::new(99)
15821                .with_text(" ")
15822                .with_channel(HIDDEN_CHANNEL),
15823            TestToken::new(1).with_text("x"),
15824            TestToken::eof("parser-test", 2, 1, 2),
15825        ]);
15826
15827        let tree = parser
15828            .parse_atn_rule(&atn, 0)
15829            .expect("artificial parser rule should parse");
15830        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
15831            panic!("rule node should be present");
15832        };
15833        assert_eq!(
15834            rule.start()
15835                .expect("rule should have a start token")
15836                .token_type(),
15837            1
15838        );
15839    }
15840
15841    #[test]
15842    fn parser_action_after_eof_stops_at_eof_token() {
15843        let atn = eof_then_action_atn();
15844        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15845
15846        let (_, actions) = parser
15847            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15848            .expect("EOF action rule should parse");
15849
15850        assert_eq!(actions.len(), 1);
15851        assert_eq!(actions[0].stop_index(), Some(0));
15852        assert_eq!(
15853            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
15854            ""
15855        );
15856    }
15857
15858    #[test]
15859    fn after_action_stop_uses_rule_context_stop_not_cursor() {
15860        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
15861        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
15862        // but the rule did not consume it. The @after stop must follow the rule
15863        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
15864        let mut id = TestToken::new(1).with_text("x");
15865        id.set_token_index(0);
15866        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
15867        eof.set_token_index(1);
15868        let mut parser = mini_parser(vec![id.clone(), eof]);
15869        // Advance the cursor onto EOF, as it would be after `a` matched ID.
15870        parser.consume();
15871        assert_eq!(parser.la(1), TOKEN_EOF);
15872
15873        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
15874        // exactly what finish_rule(consumed_eof = false) records.
15875        let mut ctx = ParserRuleContext::new(0, 0);
15876        parser.set_context_stop(
15877            &mut ctx,
15878            parser.token_id_at(0).expect("ID token should be buffered"),
15879        );
15880        let tree = parser.rule_node(ctx);
15881
15882        let current_index = parser.input.index();
15883        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
15884        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
15885        // ...but the tree-aware helper follows the rule context stop (ID).
15886        assert_eq!(
15887            parser.after_action_stop_index_for_tree(tree, current_index),
15888            Some(0)
15889        );
15890    }
15891
15892    #[test]
15893    fn after_action_start_uses_rule_context_start_not_cursor() {
15894        // A rule that begins after leading hidden-channel tokens: the rule context
15895        // start (set by `enter_rule`) is the first visible token, not the raw cursor
15896        // that may still point at the hidden prefix. The @after start must follow
15897        // the context start so `$start`/`$text` excludes the hidden prefix.
15898        let mut parser = mini_parser(vec![
15899            TestToken::new(9)
15900                .with_text(" ")
15901                .with_channel(HIDDEN_CHANNEL),
15902            TestToken::new(9)
15903                .with_text(" ")
15904                .with_channel(HIDDEN_CHANNEL),
15905            TestToken::new(1).with_text("x"),
15906            TestToken::eof("parser-test", 3, 1, 3),
15907        ]);
15908
15909        let mut ctx = ParserRuleContext::new(0, 0);
15910        parser.set_context_start(
15911            &mut ctx,
15912            parser.token_id_at(2).expect("ID token should be buffered"),
15913        );
15914        let tree = parser.rule_node(ctx);
15915
15916        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
15917        // ...but the tree-aware helper follows the rule context start (index 2).
15918        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
15919
15920        // With no rule start recorded, it falls back to the provided index.
15921        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
15922        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
15923    }
15924
15925    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
15926        FastRecognizeOutcome {
15927            index,
15928            consumed_eof,
15929            diagnostics: DiagnosticSeqId::EMPTY,
15930            deferred_nodes: FastDeferredNodeId::EMPTY,
15931            nodes: NodeSeqId(marker),
15932        }
15933    }
15934
15935    #[test]
15936    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
15937        let mut outcomes = vec![
15938            clean_fast_outcome(4, false, 0),
15939            clean_fast_outcome(2, false, 1),
15940            clean_fast_outcome(4, false, 2),
15941            clean_fast_outcome(4, true, 3),
15942            clean_fast_outcome(2, false, 4),
15943        ];
15944        let mut scratch = FastOutcomeDedupScratch::default();
15945
15946        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
15947
15948        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
15949        assert_eq!(
15950            outcomes
15951                .iter()
15952                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
15953                .collect::<Vec<_>>(),
15954            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
15955        );
15956        assert!(scratch.dense_words.is_empty());
15957        assert!(scratch.sparse_keys.is_empty());
15958    }
15959
15960    #[test]
15961    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
15962        let mut scratch = FastOutcomeDedupScratch::default();
15963        let mut outcomes = (100..109)
15964            .flat_map(|index| {
15965                [
15966                    clean_fast_outcome(
15967                        index,
15968                        false,
15969                        u32::try_from(index).expect("test index fits in u32"),
15970                    ),
15971                    clean_fast_outcome(index, false, u32::MAX),
15972                ]
15973            })
15974            .collect();
15975
15976        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
15977
15978        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
15979        assert_eq!(outcomes.len(), 9);
15980        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
15981        let dense_capacity = scratch.dense_words.capacity();
15982
15983        let mut reused = (1_000..1_009)
15984            .map(|index| {
15985                clean_fast_outcome(
15986                    index,
15987                    false,
15988                    u32::try_from(index).expect("test index fits in u32"),
15989                )
15990            })
15991            .collect();
15992        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
15993
15994        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
15995        assert_eq!(reused.len(), 9);
15996        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
15997    }
15998
15999    #[test]
16000    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
16001        let mut scratch = FastOutcomeDedupScratch::default();
16002        let sparse_indexes = [
16003            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
16004        ];
16005        let mut outcomes = sparse_indexes
16006            .into_iter()
16007            .chain([400_000])
16008            .enumerate()
16009            .map(|(marker, index)| {
16010                clean_fast_outcome(
16011                    index,
16012                    false,
16013                    u32::try_from(marker).expect("test marker fits in u32"),
16014                )
16015            })
16016            .collect();
16017
16018        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16019
16020        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16021        assert_eq!(outcomes.len(), sparse_indexes.len());
16022        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
16023        let sparse_capacity = scratch.sparse_keys.capacity();
16024
16025        let mut reused = sparse_indexes
16026            .into_iter()
16027            .map(|index| {
16028                clean_fast_outcome(
16029                    index,
16030                    false,
16031                    u32::try_from(index).expect("test index fits in u32"),
16032                )
16033            })
16034            .collect();
16035        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16036
16037        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16038        assert_eq!(reused.len(), sparse_indexes.len());
16039        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
16040    }
16041
16042    #[test]
16043    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
16044        let mut scratch = FastOutcomeDedupScratch::default();
16045        scratch
16046            .sparse_keys
16047            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
16048        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16049        let mut outcomes = (0..9)
16050            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
16051            .collect();
16052
16053        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16054
16055        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16056        assert!(scratch.sparse_keys.is_empty());
16057        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16058    }
16059
16060    #[test]
16061    fn fast_outcome_selection_respects_sll_tie_order() {
16062        let mut arena = RecognitionArena::default();
16063        let first = FastRecognizeOutcome {
16064            index: 1,
16065            consumed_eof: false,
16066            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16067                line: 1,
16068                column: 0,
16069                message: "mismatched input 'x'".to_owned(),
16070            }]),
16071            deferred_nodes: FastDeferredNodeId::EMPTY,
16072            nodes: NodeSeqId::EMPTY,
16073        };
16074        let second = FastRecognizeOutcome {
16075            index: first.index,
16076            consumed_eof: first.consumed_eof,
16077            diagnostics: DiagnosticSeqId::EMPTY,
16078            deferred_nodes: FastDeferredNodeId::EMPTY,
16079            nodes: NodeSeqId::EMPTY,
16080        };
16081
16082        let selected = select_best_fast_outcome(
16083            [first, second].into_iter(),
16084            PredictionMode::Sll,
16085            None,
16086            |_| panic!("caller-follow token probe should not run"),
16087            &arena,
16088        )
16089        .expect("one outcome should be selected");
16090        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
16091        let eof_second = FastRecognizeOutcome {
16092            index: second.index,
16093            consumed_eof: true,
16094            diagnostics: DiagnosticSeqId::EMPTY,
16095            deferred_nodes: FastDeferredNodeId::EMPTY,
16096            nodes: NodeSeqId::EMPTY,
16097        };
16098        let selected = select_best_fast_outcome(
16099            [first, eof_second].into_iter(),
16100            PredictionMode::Sll,
16101            None,
16102            |_| panic!("caller-follow token probe should not run"),
16103            &arena,
16104        )
16105        .expect("one outcome should be selected");
16106        assert!(!selected.consumed_eof);
16107        let selected = select_best_fast_outcome(
16108            [first, second].into_iter(),
16109            PredictionMode::Ll,
16110            None,
16111            |_| panic!("caller-follow token probe should not run"),
16112            &arena,
16113        )
16114        .expect("one outcome should be selected");
16115        assert!(selected.diagnostics.is_empty());
16116    }
16117
16118    #[test]
16119    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
16120        let mut arena = RecognitionArena::default();
16121        let first = FastRecognizeOutcome {
16122            index: 3,
16123            consumed_eof: false,
16124            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16125                line: 1,
16126                column: 0,
16127                message: "mismatched input 'x' expecting 'a'".to_owned(),
16128            }]),
16129            deferred_nodes: FastDeferredNodeId::EMPTY,
16130            nodes: NodeSeqId::EMPTY,
16131        };
16132        let same_rank = FastRecognizeOutcome {
16133            index: first.index,
16134            consumed_eof: first.consumed_eof,
16135            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16136                line: 1,
16137                column: 0,
16138                message: "mismatched input 'x' expecting 'b'".to_owned(),
16139            }]),
16140            deferred_nodes: FastDeferredNodeId::EMPTY,
16141            nodes: NodeSeqId::EMPTY,
16142        };
16143        let better_rank = FastRecognizeOutcome {
16144            index: first.index,
16145            consumed_eof: first.consumed_eof,
16146            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16147                line: 1,
16148                column: 0,
16149                message: "missing 'a' at 'x'".to_owned(),
16150            }]),
16151            deferred_nodes: FastDeferredNodeId::EMPTY,
16152            nodes: NodeSeqId::EMPTY,
16153        };
16154        let mut outcomes = vec![first, same_rank, better_rank];
16155
16156        dedupe_fast_outcomes(&mut outcomes, &arena);
16157
16158        assert_eq!(outcomes.len(), 2);
16159        assert_eq!(
16160            arena
16161                .diagnostics(outcomes[0].diagnostics)
16162                .next()
16163                .expect("first diagnostic")
16164                .message,
16165            "mismatched input 'x' expecting 'a'"
16166        );
16167        assert_eq!(
16168            arena
16169                .diagnostics(outcomes[1].diagnostics)
16170                .next()
16171                .expect("second diagnostic")
16172                .message,
16173            "missing 'a' at 'x'"
16174        );
16175    }
16176
16177    #[test]
16178    fn fast_outcome_selection_prefers_generated_caller_follow() {
16179        let arena = RecognitionArena::default();
16180        let earlier = FastRecognizeOutcome {
16181            index: 7,
16182            consumed_eof: false,
16183            diagnostics: DiagnosticSeqId::EMPTY,
16184            deferred_nodes: FastDeferredNodeId::EMPTY,
16185            nodes: NodeSeqId::EMPTY,
16186        };
16187        let later = FastRecognizeOutcome {
16188            index: 8,
16189            consumed_eof: false,
16190            diagnostics: DiagnosticSeqId::EMPTY,
16191            deferred_nodes: FastDeferredNodeId::EMPTY,
16192            nodes: NodeSeqId::EMPTY,
16193        };
16194        let mut follow = TokenBitSet::default();
16195        follow.insert(5);
16196
16197        let selected = select_best_fast_outcome(
16198            [later, earlier].into_iter(),
16199            PredictionMode::Ll,
16200            Some(&follow),
16201            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
16202            &arena,
16203        )
16204        .expect("one outcome should be selected");
16205        assert_eq!(selected.index, 7);
16206
16207        let selected = select_best_fast_outcome(
16208            [later, earlier].into_iter(),
16209            PredictionMode::Ll,
16210            Some(&follow),
16211            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
16212            &arena,
16213        )
16214        .expect("one outcome should be selected");
16215        assert_eq!(selected.index, 8);
16216
16217        let indented_next_statement = FastRecognizeOutcome {
16218            index: 9,
16219            consumed_eof: false,
16220            diagnostics: DiagnosticSeqId::EMPTY,
16221            deferred_nodes: FastDeferredNodeId::EMPTY,
16222            nodes: NodeSeqId::EMPTY,
16223        };
16224        let selected = select_best_fast_outcome(
16225            [indented_next_statement, earlier].into_iter(),
16226            PredictionMode::Ll,
16227            Some(&follow),
16228            |index| {
16229                let is_boundary = index == 7;
16230                let is_boundary_gap = matches!(index, 7 | 8);
16231                (
16232                    if index == 7 { 5 } else { TOKEN_EOF },
16233                    is_boundary,
16234                    is_boundary_gap,
16235                )
16236            },
16237            &arena,
16238        )
16239        .expect("one outcome should be selected");
16240        assert_eq!(selected.index, 7);
16241
16242        let continuation = FastRecognizeOutcome {
16243            index: 10,
16244            consumed_eof: false,
16245            diagnostics: DiagnosticSeqId::EMPTY,
16246            deferred_nodes: FastDeferredNodeId::EMPTY,
16247            nodes: NodeSeqId::EMPTY,
16248        };
16249        let selected = select_best_fast_outcome(
16250            [continuation, earlier].into_iter(),
16251            PredictionMode::Ll,
16252            Some(&follow),
16253            |index| {
16254                let is_boundary = matches!(index, 7 | 9);
16255                (
16256                    if index == 7 { 5 } else { TOKEN_EOF },
16257                    is_boundary,
16258                    is_boundary,
16259                )
16260            },
16261            &arena,
16262        )
16263        .expect("one outcome should be selected");
16264        assert_eq!(selected.index, 10);
16265
16266        let selected = select_best_fast_outcome(
16267            [earlier, later].into_iter(),
16268            PredictionMode::Sll,
16269            Some(&follow),
16270            |_| panic!("caller-follow token probe should not run in SLL mode"),
16271            &arena,
16272        )
16273        .expect("one outcome should be selected");
16274        assert_eq!(selected.index, 8);
16275    }
16276
16277    #[test]
16278    fn caller_follow_boundary_text_requires_separator_shape() {
16279        assert!(is_caller_follow_boundary_text(";"));
16280        assert!(is_caller_follow_boundary_text("\n"));
16281        assert!(is_caller_follow_boundary_text("\r\n  "));
16282        assert!(is_caller_follow_boundary_text(";\n"));
16283        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
16284        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
16285        assert!(!is_caller_follow_boundary_text("identifier"));
16286        assert!(is_caller_follow_boundary_gap_text(" \t "));
16287        assert!(is_caller_follow_boundary_gap_text("\n  "));
16288        assert!(is_caller_follow_boundary_gap_text(";\t"));
16289        assert!(!is_caller_follow_boundary_gap_text(
16290            "\"\"\"line1\nline2\"\"\""
16291        ));
16292        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
16293    }
16294
16295    #[test]
16296    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
16297        let mut parser = mini_parser(vec![
16298            TestToken::new(5).with_text("\n"),
16299            TestToken::new(6)
16300                .with_text("// comment\n")
16301                .with_channel(HIDDEN_CHANNEL),
16302            TestToken::new(1).with_text("x"),
16303            TestToken::eof("parser-test", 1, 2, 0),
16304        ]);
16305
16306        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
16307        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
16308        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
16309    }
16310
16311    #[test]
16312    fn caller_follow_token_info_uses_stream_visible_channel() {
16313        let source = Source {
16314            tokens: vec![
16315                TestToken::new(5).with_text("\n").with_channel(2),
16316                TestToken::new(1).with_text("x").with_channel(2),
16317                TestToken::new(6)
16318                    .with_text("// comment\n")
16319                    .with_channel(HIDDEN_CHANNEL),
16320                TestToken::eof("parser-test", 1, 2, 0),
16321            ],
16322            index: 0,
16323        };
16324        let data = RecognizerData::new(
16325            "Mini.g4",
16326            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16327        );
16328        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
16329
16330        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
16331        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
16332        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
16333    }
16334
16335    #[test]
16336    fn reset_per_parse_caches_clears_state_expected_token_cache() {
16337        let atn = token_then_eof_atn();
16338        let mut parser = mini_parser(Vec::new());
16339
16340        let _ = parser.cached_state_expected_token_set(&atn, 0);
16341        assert!(!parser.state_expected_token_cache.is_empty());
16342
16343        parser.reset_per_parse_caches();
16344        assert!(parser.state_expected_token_cache.is_empty());
16345    }
16346
16347    #[test]
16348    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
16349        let source = Source {
16350            tokens: vec![
16351                TestToken::new(1).with_text("x"),
16352                TestToken::eof("parser-test", 1, 1, 1),
16353            ],
16354            index: 0,
16355        };
16356        let data = RecognizerData::new(
16357            "Mini.g4",
16358            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16359        );
16360        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
16361        let expected = ExpectedTokens {
16362            index: Some(0),
16363            symbols: BTreeSet::new(),
16364            no_viable: None,
16365        };
16366
16367        let (_, message) = parser.expected_error_message(0, 0, &expected);
16368
16369        assert_eq!(message, "mismatched input 'x'");
16370    }
16371
16372    #[test]
16373    fn eof_rule_stop_index_points_at_eof_token() {
16374        let source = Source {
16375            tokens: vec![
16376                TestToken::new(1).with_text("x"),
16377                TestToken::eof("parser-test", 1, 1, 1),
16378            ],
16379            index: 0,
16380        };
16381        let data = RecognizerData::new(
16382            "Mini.g4",
16383            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16384        );
16385        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
16386
16387        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
16388        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
16389    }
16390
16391    #[test]
16392    fn generated_parser_action_uses_current_rule_stop_boundary() {
16393        let mut parser = mini_parser(vec![
16394            TestToken::new(1).with_text("x"),
16395            TestToken::eof("parser-test", 1, 1, 1),
16396        ]);
16397
16398        parser.match_token(1).expect("token should match");
16399        let action = parser.parser_action_at_current(7, 0, 0, false);
16400        assert_eq!(action.source_state(), 7);
16401        assert_eq!(action.rule_index(), 0);
16402        assert_eq!(action.start_index(), 0);
16403        assert_eq!(action.stop_index(), Some(0));
16404
16405        parser.match_eof().expect("EOF should match");
16406        let action = parser.parser_action_at_current(8, 0, 0, true);
16407        assert_eq!(action.stop_index(), Some(1));
16408    }
16409
16410    #[test]
16411    fn folds_left_recursive_boundary_into_rule_node() {
16412        let mut arena = RecognitionArena::default();
16413        let first = arena.push_node(ArenaRecognizedNode::Token {
16414            token: TokenId::try_from(0).expect("test token ID"),
16415        });
16416        let boundary =
16417            arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: 1 });
16418        let second = arena.push_node(ArenaRecognizedNode::Token {
16419            token: TokenId::try_from(1).expect("test token ID"),
16420        });
16421        let mut nodes = NodeSeqId::EMPTY;
16422        for node in [first, boundary, second].into_iter().rev() {
16423            nodes = arena.prepend(nodes, node);
16424        }
16425
16426        let folded = arena.fold_left_recursive_boundaries(nodes);
16427        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
16428
16429        assert_eq!(folded_nodes.len(), 2);
16430        let ArenaRecognizedNode::Rule {
16431            rule_index,
16432            invoking_state,
16433            start_index,
16434            stop_index,
16435            children,
16436            ..
16437        } = arena.node(folded_nodes[0])
16438        else {
16439            panic!("first folded node should be a rule");
16440        };
16441        assert_eq!(rule_index, 1);
16442        assert_eq!(invoking_state, -1);
16443        assert_eq!(start_index, 0);
16444        assert_eq!(stop_index, Some(0));
16445        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
16446        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
16447
16448        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
16449        assert_eq!(
16450            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16451            (4, 3, 1)
16452        );
16453        assert_eq!(
16454            (stats.total_links, stats.live_links, stats.dead_links),
16455            (9, 3, 6)
16456        );
16457    }
16458
16459    #[test]
16460    fn recognition_arena_reports_live_dead_and_retained_capacity() {
16461        let mut arena = RecognitionArena::default();
16462        let token = arena.push_node(ArenaRecognizedNode::Token {
16463            token: TokenId::try_from(0).expect("test token ID"),
16464        });
16465        let extra = arena.push_extra(RecognitionExtra::MissingToken {
16466            token_type: 2,
16467            at_index: 1,
16468            text: "<missing X>".to_owned(),
16469        });
16470        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
16471        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
16472            token: TokenId::try_from(1).expect("test token ID"),
16473        });
16474        let mut live = NodeSeqId::EMPTY;
16475        live = arena.prepend(live, missing);
16476        live = arena.prepend(live, token);
16477        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
16478        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
16479            line: 1,
16480            column: 0,
16481            message: "missing X".to_owned(),
16482        }]);
16483        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
16484            line: 1,
16485            column: 1,
16486            message: "discarded".to_owned(),
16487        }]);
16488        let deferred_children = arena.deferred_fragment(live);
16489        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
16490            rule_index: 0,
16491            invoking_state: -1,
16492            start_index: 0,
16493            stop_index: Some(1),
16494            deferred_children,
16495            children: NodeSeqId::EMPTY,
16496        });
16497
16498        let stats = arena.stats(live, live_diagnostics);
16499
16500        assert_eq!(
16501            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16502            (3, 2, 1)
16503        );
16504        assert_eq!(
16505            (stats.total_links, stats.live_links, stats.dead_links),
16506            (5, 3, 2)
16507        );
16508        assert_eq!(
16509            (stats.total_extras, stats.live_extras, stats.dead_extras),
16510            (3, 2, 1)
16511        );
16512        assert!(size_of::<SeqLink>() <= 8);
16513        assert!(size_of::<DiagnosticLink>() <= 8);
16514        assert!(size_of::<FastDeferredNode>() <= 12);
16515        assert!(size_of::<FastDeferredRule>() <= 28);
16516        assert!(size_of::<FastRecognizeOutcome>() <= 24);
16517        let capacities = (
16518            stats.node_capacity,
16519            stats.link_capacity,
16520            stats.extra_capacity,
16521        );
16522        let deferred_capacities = (
16523            arena.deferred_nodes.capacity(),
16524            arena.deferred_rules.capacity(),
16525        );
16526
16527        arena.reset();
16528        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
16529        assert_eq!(
16530            (reset.total_nodes, reset.total_links, reset.total_extras),
16531            (0, 0, 0)
16532        );
16533        assert_eq!(
16534            (
16535                reset.node_capacity,
16536                reset.link_capacity,
16537                reset.extra_capacity,
16538            ),
16539            capacities
16540        );
16541        assert!(arena.deferred_nodes.is_empty());
16542        assert!(arena.deferred_rules.is_empty());
16543        assert_eq!(
16544            (
16545                arena.deferred_nodes.capacity(),
16546                arena.deferred_rules.capacity(),
16547            ),
16548            deferred_capacities
16549        );
16550    }
16551
16552    #[test]
16553    fn parser_computes_recognition_arena_stats_on_demand() {
16554        let mut parser = mini_parser(Vec::new());
16555        let live = parser
16556            .recognition_arena
16557            .push_node(ArenaRecognizedNode::Token {
16558                token: TokenId::try_from(0).expect("test token ID"),
16559            });
16560        let discarded = parser
16561            .recognition_arena
16562            .push_node(ArenaRecognizedNode::ErrorToken {
16563                token: TokenId::try_from(1).expect("test token ID"),
16564            });
16565        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
16566        let _discarded_root = parser
16567            .recognition_arena
16568            .prepend(NodeSeqId::EMPTY, discarded);
16569        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
16570
16571        let stats = parser.recognition_arena_stats();
16572
16573        assert_eq!(
16574            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16575            (2, 1, 1)
16576        );
16577        assert_eq!(
16578            (stats.total_links, stats.live_links, stats.dead_links),
16579            (2, 1, 1)
16580        );
16581    }
16582
16583    #[test]
16584    fn recognition_arena_drops_capacity_above_retention_limit() {
16585        let mut storage = Vec::<u8>::with_capacity(4);
16586        storage.extend([1, 2, 3]);
16587
16588        reset_arena_vec(&mut storage, 3);
16589
16590        assert!(storage.is_empty());
16591        assert_eq!(storage.capacity(), 0);
16592    }
16593
16594    #[test]
16595    fn recognition_arena_concatenates_diagnostics_in_source_order() {
16596        let mut arena = RecognitionArena::default();
16597        let prefix = arena.diagnostic_sequence([
16598            ParserDiagnostic {
16599                line: 1,
16600                column: 0,
16601                message: "first".to_owned(),
16602            },
16603            ParserDiagnostic {
16604                line: 1,
16605                column: 1,
16606                message: "second".to_owned(),
16607            },
16608        ]);
16609        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
16610            line: 1,
16611            column: 2,
16612            message: "third".to_owned(),
16613        }]);
16614        let extras_before = arena.extras.len();
16615
16616        let combined = arena.concat_diagnostics(prefix, suffix);
16617        let messages = arena
16618            .diagnostics(combined)
16619            .map(|diagnostic| diagnostic.message.as_str())
16620            .collect::<Vec<_>>();
16621
16622        assert_eq!(messages, ["first", "second", "third"]);
16623        assert_eq!(arena.extras.len(), extras_before);
16624    }
16625
16626    #[test]
16627    fn outcome_ties_keep_later_non_recursive_alternative() {
16628        let arena = RecognitionArena::default();
16629        let first = RecognizeOutcome {
16630            index: 1,
16631            consumed_eof: false,
16632            alt_number: 0,
16633            member_values: BTreeMap::new(),
16634            return_values: BTreeMap::new(),
16635            diagnostics: DiagnosticSeqId::EMPTY,
16636            decisions: Vec::new(),
16637            actions: vec![ParserAction::new(1, 0, 0, None)],
16638            nodes: NodeSeqId::EMPTY,
16639        };
16640        let second = RecognizeOutcome {
16641            actions: vec![ParserAction::new(2, 0, 0, None)],
16642            ..first.clone()
16643        };
16644
16645        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
16646            .expect("one outcome should be selected");
16647        assert_eq!(selected.actions[0].source_state(), 2);
16648    }
16649
16650    #[test]
16651    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
16652        let arena = RecognitionArena::default();
16653        let first = RecognizeOutcome {
16654            index: 1,
16655            consumed_eof: false,
16656            alt_number: 0,
16657            member_values: BTreeMap::new(),
16658            return_values: BTreeMap::new(),
16659            diagnostics: DiagnosticSeqId::EMPTY,
16660            decisions: Vec::new(),
16661            actions: vec![ParserAction::new(1, 0, 0, None)],
16662            nodes: NodeSeqId::EMPTY,
16663        };
16664        let second = RecognizeOutcome {
16665            actions: vec![
16666                ParserAction::new(2, 0, 0, None),
16667                ParserAction::new(3, 0, 0, None),
16668            ],
16669            ..first.clone()
16670        };
16671
16672        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
16673            .expect("one outcome should be selected");
16674        assert_eq!(selected.actions.len(), 2);
16675    }
16676
16677    #[test]
16678    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
16679        let arena = RecognitionArena::default();
16680        let first = RecognizeOutcome {
16681            index: 7,
16682            consumed_eof: false,
16683            alt_number: 0,
16684            member_values: BTreeMap::new(),
16685            return_values: BTreeMap::new(),
16686            diagnostics: DiagnosticSeqId::EMPTY,
16687            decisions: vec![1, 0],
16688            actions: vec![
16689                ParserAction::new(23, 2, 2, Some(4)),
16690                ParserAction::new(23, 2, 0, Some(6)),
16691            ],
16692            nodes: NodeSeqId::EMPTY,
16693        };
16694        let second = RecognizeOutcome {
16695            decisions: vec![0, 1],
16696            actions: vec![
16697                ParserAction::new(23, 2, 2, Some(6)),
16698                ParserAction::new(23, 2, 0, Some(6)),
16699            ],
16700            ..first.clone()
16701        };
16702
16703        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
16704            .expect("one outcome should be selected");
16705        assert_eq!(selected.actions[0].stop_index(), Some(6));
16706    }
16707
16708    #[test]
16709    fn outcome_ties_keep_first_recursive_tree_shape() {
16710        let mut arena = RecognitionArena::default();
16711        let token = arena.push_node(ArenaRecognizedNode::Token {
16712            token: TokenId::try_from(0).expect("test token ID"),
16713        });
16714        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
16715        let inner = arena.push_node(ArenaRecognizedNode::Rule {
16716            rule_index: 1,
16717            invoking_state: -1,
16718            alt_number: 0,
16719            start_index: 0,
16720            stop_index: Some(0),
16721            return_values: None,
16722            children: token_children,
16723        });
16724        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
16725        let outer = arena.push_node(ArenaRecognizedNode::Rule {
16726            rule_index: 1,
16727            invoking_state: -1,
16728            alt_number: 0,
16729            start_index: 0,
16730            stop_index: Some(0),
16731            return_values: None,
16732            children: inner_children,
16733        });
16734        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
16735        let first = RecognizeOutcome {
16736            index: 1,
16737            consumed_eof: false,
16738            alt_number: 0,
16739            member_values: BTreeMap::new(),
16740            return_values: BTreeMap::new(),
16741            diagnostics: DiagnosticSeqId::EMPTY,
16742            decisions: Vec::new(),
16743            actions: vec![ParserAction::new(1, 0, 0, None)],
16744            nodes: recursive_nodes,
16745        };
16746        let second = RecognizeOutcome {
16747            index: 1,
16748            consumed_eof: false,
16749            alt_number: 0,
16750            member_values: BTreeMap::new(),
16751            return_values: BTreeMap::new(),
16752            diagnostics: DiagnosticSeqId::EMPTY,
16753            decisions: Vec::new(),
16754            actions: vec![ParserAction::new(2, 0, 0, None)],
16755            nodes: recursive_nodes,
16756        };
16757
16758        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
16759            .expect("one outcome should be selected");
16760        assert_eq!(selected.actions[0].source_state(), 1);
16761    }
16762
16763    #[test]
16764    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
16765        let mut arena = RecognitionArena::default();
16766        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
16767            line: 1,
16768            column: 3,
16769            message: "missing 'Y' at '<EOF>'".to_owned(),
16770        }]);
16771        let first_alt = RecognizeOutcome {
16772            index: 2,
16773            consumed_eof: true,
16774            alt_number: 0,
16775            member_values: BTreeMap::new(),
16776            return_values: BTreeMap::new(),
16777            diagnostics: recovered_diagnostics,
16778            decisions: vec![0],
16779            actions: vec![ParserAction::new(1, 0, 0, None)],
16780            nodes: NodeSeqId::EMPTY,
16781        };
16782        let second_alt = RecognizeOutcome {
16783            diagnostics: DiagnosticSeqId::EMPTY,
16784            decisions: vec![1],
16785            actions: vec![ParserAction::new(2, 0, 0, None)],
16786            ..first_alt.clone()
16787        };
16788
16789        let selected = select_best_outcome(
16790            [second_alt, first_alt].into_iter(),
16791            PredictionMode::Sll,
16792            &arena,
16793        )
16794        .expect("one outcome should be selected");
16795        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
16796        assert_eq!(selected.decisions, [0]);
16797    }
16798}