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 memo entries are reusable
111/// enough to keep caching. High-cardinality parses mostly produce one-shot
112/// entries; compact ambiguous loops repeatedly hit the same keys.
113const CLEAN_MEMO_PROBE_LIMIT: usize = 4096;
114const CLEAN_MEMO_REPEAT_LIMIT: usize = 8;
115/// Sparse parses periodically reopen the bounded probe so a repeat-heavy
116/// region that starts later in the token stream can promote memoization.
117const CLEAN_MEMO_REPROBE_INTERVAL: usize = 262_144;
118const FAST_RECOGNIZE_VISITING_CAPACITY: usize = 256;
119const FAST_RECOGNIZE_MIN_MEMO_CAPACITY: usize = 256;
120const FAST_RECOGNIZE_MAX_MEMO_CAPACITY: usize = 524_288;
121const FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY: usize = 65_536;
122
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124enum CleanMemoMode {
125    Probe,
126    Promote,
127    Sparse,
128}
129
130fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
131    intervals
132        .iter()
133        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
134}
135
136fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
137    let mut symbols = BTreeSet::new();
138    for (start, stop) in intervals {
139        symbols.extend(*start..=*stop);
140    }
141    symbols
142}
143
144fn interval_complement_symbols(
145    intervals: &[(i32, i32)],
146    min_vocabulary: i32,
147    max_vocabulary: i32,
148) -> BTreeSet<i32> {
149    (min_vocabulary..=max_vocabulary)
150        .filter(|symbol| !interval_set_contains(intervals, *symbol))
151        .collect()
152}
153
154#[cfg(feature = "perf-counters")]
155mod perf_counters {
156    use std::cell::Cell;
157    thread_local! {
158        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
159        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
160        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
161        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
162        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
163        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
164        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
165        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
166        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
167        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
168        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
169        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
170        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
171    }
172    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
173        c.with(|v| v.set(v.get() + n));
174    }
175    thread_local! {
176        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
177        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
178        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
179        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
180        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
181        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
182        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
183        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
184        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
185        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
186        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
187    }
188    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
189        [
190            ("rfs_calls", RFS_CALLS.with(Cell::get)),
191            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
192            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
193            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
194            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
195            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
196            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
197            (
198                "outcome_dedupe_inputs",
199                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
200            ),
201            (
202                "outcome_dedupe_removed",
203                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
204            ),
205            (
206                "outcome_dedupe_inline",
207                OUTCOME_DEDUPE_INLINE.with(Cell::get),
208            ),
209            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
210            (
211                "outcome_dedupe_sparse",
212                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
213            ),
214            (
215                "outcome_dedupe_dense_words",
216                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
217            ),
218            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
219            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
220            (
221                "atom_range_transitions",
222                ATOM_RANGE_TRANSITIONS.with(Cell::get),
223            ),
224            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
225            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
226            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
227            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
228            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
229            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
230            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
231            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
232        ]
233    }
234    pub fn reset() {
235        RFS_CALLS.with(|c| c.set(0));
236        RFS_MEMO_HITS.with(|c| c.set(0));
237        RFS_MEMO_MISSES.with(|c| c.set(0));
238        RFS_VISITING_CYCLE.with(|c| c.set(0));
239        MEMO_INSERTED.with(|c| c.set(0));
240        OUTCOMES_PUSHED.with(|c| c.set(0));
241        OUTCOMES_CLONED.with(|c| c.set(0));
242        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
243        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
244        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
245        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
246        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
247        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
248        EPSILON_TRANSITIONS.with(|c| c.set(0));
249        RULE_TRANSITIONS.with(|c| c.set(0));
250        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
251        SINGLE_TRANS_BODY.with(|c| c.set(0));
252        MULTI_TRANS_BODY.with(|c| c.set(0));
253        SINGLE_TRANS_RULE.with(|c| c.set(0));
254        SINGLE_TRANS_ATOM.with(|c| c.set(0));
255        SINGLE_TRANS_OTHER.with(|c| c.set(0));
256        OUTCOMES_RETURN_0.with(|c| c.set(0));
257        OUTCOMES_RETURN_1.with(|c| c.set(0));
258        OUTCOMES_RETURN_N.with(|c| c.set(0));
259    }
260    pub fn dump() {
261        for (name, value) in snapshot() {
262            #[allow(clippy::print_stderr)]
263            {
264                eprintln!("perf {name}={value}");
265            }
266        }
267    }
268}
269
270#[cfg(feature = "perf-counters")]
271pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
272/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
273/// fast recognizer has probed far enough that per-token stream sync dominates.
274/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
275/// switching long inputs to the cheaper filled-stream path before large fanout.
276const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
277/// Parser semantic action reached while recognizing one ATN path.
278///
279/// Generated parsers use `source_state` to dispatch back to the grammar action
280/// rendered for that ATN action transition. The token interval is the current
281/// rule's input span at the action site, which covers common target templates
282/// such as `$text`. Rule-init actions do not have an ATN action source state,
283/// so they are marked separately and may carry an ATN state for expected-token
284/// rendering.
285#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
286pub struct ParserAction {
287    source_state: usize,
288    rule_index: usize,
289    start_index: usize,
290    stop_index: Option<usize>,
291    rule_init: bool,
292    expected_state: Option<usize>,
293}
294
295impl ParserAction {
296    /// Creates an action event for a recognized parser path.
297    pub const fn new(
298        source_state: usize,
299        rule_index: usize,
300        start_index: usize,
301        stop_index: Option<usize>,
302    ) -> Self {
303        Self {
304            source_state,
305            rule_index,
306            start_index,
307            stop_index,
308            rule_init: false,
309            expected_state: None,
310        }
311    }
312
313    /// Creates an action event for a rule-level `@init` action.
314    pub const fn new_rule_init(
315        rule_index: usize,
316        start_index: usize,
317        expected_state: Option<usize>,
318    ) -> Self {
319        Self {
320            source_state: usize::MAX,
321            rule_index,
322            start_index,
323            stop_index: None,
324            rule_init: true,
325            expected_state,
326        }
327    }
328
329    /// ATN state that owns the semantic-action transition.
330    pub const fn source_state(&self) -> usize {
331        self.source_state
332    }
333
334    /// Grammar rule index recorded by the serialized ATN action transition.
335    pub const fn rule_index(&self) -> usize {
336        self.rule_index
337    }
338
339    /// Token-stream index where the active rule began.
340    pub const fn start_index(&self) -> usize {
341        self.start_index
342    }
343
344    /// Last token-stream index consumed before the action was reached.
345    pub const fn stop_index(&self) -> Option<usize> {
346        self.stop_index
347    }
348
349    /// Reports whether this event represents a rule-level `@init` action.
350    pub const fn is_rule_init(&self) -> bool {
351        self.rule_init
352    }
353
354    /// ATN state used to compute expected-token display for this action.
355    pub const fn expected_state(&self) -> Option<usize> {
356        self.expected_state
357    }
358}
359
360/// Runtime view passed to parser semantic hooks.
361///
362/// The context is intentionally read-only with respect to parser structure:
363/// predicates may run speculatively during prediction, and hooks can be called
364/// more than once for paths that are later abandoned. Lookahead methods may
365/// buffer tokens from the underlying token source, matching normal parser
366/// prediction behavior.
367pub struct ParserSemCtx<'a, S>
368where
369    S: TokenSource,
370{
371    input: &'a mut CommonTokenStream<S>,
372    tree_storage: &'a ParseTreeStorage,
373    rule_index: usize,
374    coordinate_index: usize,
375    rule_name: Option<String>,
376    context: Option<&'a ParserRuleContext>,
377    tree: Option<ParseTree>,
378    local_int_arg: Option<(usize, i64)>,
379    member_values: &'a BTreeMap<usize, i64>,
380    action: Option<ParserAction>,
381}
382
383impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
384where
385    S: TokenSource,
386{
387    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388        f.debug_struct("ParserSemCtx")
389            .field("rule_index", &self.rule_index)
390            .field("coordinate_index", &self.coordinate_index)
391            .field("rule_name", &self.rule_name)
392            .field("context", &self.context)
393            .field("tree", &self.tree)
394            .field("local_int_arg", &self.local_int_arg)
395            .field("member_values", &self.member_values)
396            .field("action", &self.action)
397            .finish_non_exhaustive()
398    }
399}
400
401impl<'a, S> ParserSemCtx<'a, S>
402where
403    S: TokenSource,
404{
405    /// Rule index that owns the predicate/action coordinate.
406    #[must_use]
407    pub const fn rule_index(&self) -> usize {
408        self.rule_index
409    }
410
411    /// Rule name that owns the coordinate, when recognizer metadata has it.
412    #[must_use]
413    pub fn rule_name(&self) -> Option<&str> {
414        self.rule_name.as_deref()
415    }
416
417    /// Predicate/action index inside the owning rule. Parser actions keyed only
418    /// by ATN source state report `usize::MAX` here; use [`Self::action`] for
419    /// the stable action event.
420    #[must_use]
421    pub const fn coordinate_index(&self) -> usize {
422        self.coordinate_index
423    }
424
425    /// Current token-stream index.
426    #[must_use]
427    pub fn input_index(&self) -> usize {
428        self.input.index()
429    }
430
431    /// Token type at one-based lookahead/lookbehind offset.
432    pub fn la(&mut self, offset: isize) -> i32 {
433        self.input.la(offset)
434    }
435
436    /// Token at one-based lookahead/lookbehind offset.
437    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
438        self.input.lt(offset)
439    }
440
441    /// Borrowing token view for text inspection at a one-based offset.
442    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
443        self.lt(offset)
444    }
445
446    /// Token at an absolute buffered index, including hidden/custom channels.
447    ///
448    /// Unlike [`Self::lt`], this does not apply the token stream's channel
449    /// filter and does not move its cursor. It is intended for semantic helpers
450    /// such as automatic-semicolon-insertion checks that inspect trivia
451    /// immediately before the current visible token.
452    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
453        self.input.get(index)
454    }
455
456    /// Current generated rule context, when a generated rule predicate supplied
457    /// one.
458    #[must_use]
459    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
460        self.context
461    }
462
463    /// Flat tree storage containing completed children visible to this hook.
464    #[must_use]
465    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
466        self.tree_storage
467    }
468
469    /// Canonical token store used by completed flat-tree nodes.
470    #[must_use]
471    pub const fn token_store(&self) -> &TokenStore {
472        self.input.token_store()
473    }
474
475    /// Completed parse-tree root ID passed to a replayed action hook.
476    #[must_use]
477    pub const fn tree_id(&self) -> Option<NodeId> {
478        self.tree
479    }
480
481    /// Completed parse tree passed to an action hook, if the action is being
482    /// replayed after recognition.
483    #[must_use]
484    pub fn tree(&self) -> Option<Node<'_>> {
485        self.tree
486            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
487    }
488
489    /// Integer local argument visible to this predicate coordinate.
490    #[must_use]
491    pub fn local_int_arg(&self) -> Option<i64> {
492        self.local_int_arg.map(|(_, value)| value)
493    }
494
495    /// Integer member value observed on the current speculative path.
496    #[must_use]
497    pub fn member_int(&self, member: usize) -> Option<i64> {
498        self.member_values.get(&member).copied()
499    }
500
501    /// Parser action event being replayed, when this context belongs to an
502    /// action hook.
503    #[must_use]
504    pub const fn action(&self) -> Option<ParserAction> {
505        self.action
506    }
507
508    /// Text covered by a parser action event.
509    ///
510    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
511    /// EOF the interval ends at the previous *visible* token, so trailing hidden
512    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
513    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
514    /// itself guards `start > stop`, so an empty interval yields `""`.
515    pub fn action_text(&self) -> String {
516        let Some(action) = self.action else {
517            return String::new();
518        };
519        let Some(stop) = action.stop_index() else {
520            return String::new();
521        };
522        let stop = if self
523            .input
524            .get(stop)
525            .is_some_and(|token| token.token_type() == TOKEN_EOF)
526        {
527            let Some(previous) = self.input.previous_visible_token_index(stop) else {
528                return String::new();
529            };
530            previous
531        } else {
532            stop
533        };
534        self.input.text(action.start_index(), stop)
535    }
536}
537
538/// User extension point for parser semantic predicates and actions that the
539/// metadata generator did not translate into built-in runtime metadata.
540///
541/// Returning `None`/`false` says "not handled", so the runtime falls through
542/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
543/// speculative prediction and must be replay-safe.
544pub trait SemanticHooks {
545    /// Whether generated lexers should route lifecycle callbacks through this
546    /// hook object.
547    ///
548    /// User hook implementations opt in by default. [`NoSemanticHooks`]
549    /// overrides this to keep generated lexers on the direct no-extension
550    /// token path.
551    const ENABLES_LEXER_LIFECYCLE: bool = true;
552
553    /// Whether this hook object may observe parser predicate transitions.
554    ///
555    /// Custom hooks default to conservative predicate handling so the fast
556    /// recognizer does not bypass a `sempred` implementation.
557    fn observes_parser_predicates(&self) -> bool {
558        true
559    }
560
561    fn sempred<S>(
562        &mut self,
563        ctx: &mut ParserSemCtx<'_, S>,
564        rule_index: usize,
565        pred_index: usize,
566    ) -> Option<bool>
567    where
568        S: TokenSource,
569    {
570        let _ = (ctx, rule_index, pred_index);
571        None
572    }
573
574    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
575    where
576        S: TokenSource,
577    {
578        let _ = (ctx, action);
579        false
580    }
581
582    fn lexer_sempred<I>(
583        &mut self,
584        ctx: &mut LexerSemCtx<'_, I>,
585        rule_index: usize,
586        pred_index: usize,
587    ) -> Option<bool>
588    where
589        I: CharStream,
590    {
591        let _ = (ctx, rule_index, pred_index);
592        None
593    }
594
595    /// Runs a lexer custom action on the committed lexing path. Returns whether
596    /// the hook handled the action.
597    ///
598    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
599    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
600    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
601    /// queued prefix tokens, just like the closure-based `custom_action` API.
602    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
603    /// borrow, so those mutators are inert there.)
604    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
605    where
606        I: CharStream,
607    {
608        let _ = (ctx, action);
609        false
610    }
611
612    /// Runs after runtime-owned lexer state has been reset for reuse.
613    ///
614    /// Implementations should clear extension-owned transient state here.
615    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
616    where
617        I: CharStream,
618    {
619        let _ = ctx;
620    }
621
622    /// Runs before the runtime returns a queued token or starts a new ATN
623    /// token match.
624    ///
625    /// The callback also runs between internal `skip`/`more` matches, so it
626    /// observes every point where another ATN match may start.
627    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
628    where
629        I: CharStream,
630    {
631        let _ = ctx;
632    }
633
634    /// Runs after the accepted path's portable and custom actions, but before
635    /// the token span is finalized and emitted.
636    ///
637    /// Accepted paths that selected `skip` or `more` are included, and the hook
638    /// may observe or override that pending token type.
639    ///
640    /// This callback has no synthetic ATN coordinate. It therefore also runs
641    /// for accepted rules that contain no action or predicate.
642    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
643    where
644        I: CharStream,
645    {
646        let _ = ctx;
647    }
648
649    /// Observes a token after committed lexer actions and portable commands
650    /// have run and the token has been emitted, immediately before it is
651    /// returned to the token stream.
652    ///
653    /// Hidden and custom-channel tokens are included. `skip` and intermediate
654    /// `more` matches do not produce callbacks.
655    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
656        let _ = token;
657    }
658}
659
660/// Default hook object used by parsers that do not need user-supplied
661/// semantics.
662#[derive(Clone, Copy, Debug, Default)]
663pub struct NoSemanticHooks;
664
665impl SemanticHooks for NoSemanticHooks {
666    const ENABLES_LEXER_LIFECYCLE: bool = false;
667
668    fn observes_parser_predicates(&self) -> bool {
669        false
670    }
671}
672
673/// Parser semantic predicate rendered from a supported target template.
674///
675/// The metadata recognizer evaluates these at the token-stream index where the
676/// predicate transition is reached. Unsupported or absent predicate templates
677/// remain unconditional so existing generated parsers keep their previous
678/// behavior unless the generator opts into this table.
679#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
680pub enum ParserPredicate {
681    True,
682    False,
683    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
684    FalseWithMessage {
685        message: &'static str,
686    },
687    /// Target-template test helper that reports predicate evaluation before
688    /// returning the wrapped boolean value.
689    Invoke {
690        value: bool,
691    },
692    LookaheadTextEquals {
693        offset: isize,
694        text: &'static str,
695    },
696    LookaheadNotEquals {
697        offset: isize,
698        token_type: i32,
699    },
700    /// Checks that the last two consumed visible tokens were adjacent in the
701    /// token stream. Used by C# parser predicates for split operator tokens.
702    TokenPairAdjacent,
703    /// Checks a generated parser context child by rule index and text.
704    ///
705    /// If the child is absent the predicate succeeds, matching target helpers
706    /// that treat incomplete or non-matching contexts as non-restrictive.
707    ContextChildRuleTextNotEquals {
708        rule_index: usize,
709        text: &'static str,
710    },
711    /// Compares the current rule invocation's integer argument with a literal
712    /// value from a supported `ValEquals("$i", "...")` target template.
713    LocalIntEquals {
714        value: i64,
715    },
716    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
717    /// rule invocation's integer argument.
718    LocalIntLessOrEqual {
719        value: i64,
720    },
721    /// Compares a generated parser integer member modulo a literal value.
722    MemberModuloEquals {
723        member: usize,
724        modulus: i64,
725        value: i64,
726        equals: bool,
727    },
728    /// Compares a generated parser integer member with a literal value.
729    MemberEquals {
730        member: usize,
731        value: i64,
732        equals: bool,
733    },
734}
735
736impl ParserPredicate {
737    /// Lowers the legacy predicate metadata variant into `SemIR`.
738    ///
739    /// This is the compatibility adapter for generated parsers produced while
740    /// the runtime still emitted closed enum tables. Newer generated parsers
741    /// emit `SemIR` directly.
742    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
743        match self {
744            Self::True => ir.expr(PExpr::Bool(true)),
745            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
746            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
747            Self::LookaheadTextEquals { offset, text } => {
748                let token = ir.expr(PExpr::TokenText(offset));
749                let text = ir.intern(text);
750                let text = ir.expr(PExpr::Str(text));
751                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
752            }
753            Self::LookaheadNotEquals { offset, token_type } => {
754                let actual = ir.expr(PExpr::La(offset));
755                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
756                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
757            }
758            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
759            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
760                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
761                let expected = ir.intern(text);
762                let expected = ir.expr(PExpr::Str(expected));
763                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
764            }
765            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
766            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
767            Self::MemberModuloEquals {
768                member,
769                modulus,
770                value,
771                equals,
772            } => {
773                if modulus == 0 {
774                    return ir.expr(PExpr::Bool(false));
775                }
776                let member = ir.expr(PExpr::Member(member));
777                let modulus = ir.expr(PExpr::Int(modulus));
778                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
779                let expected = ir.expr(PExpr::Int(value));
780                ir.expr(PExpr::Cmp(
781                    if equals { CmpOp::Eq } else { CmpOp::Ne },
782                    actual,
783                    expected,
784                ))
785            }
786            Self::MemberEquals {
787                member,
788                value,
789                equals,
790            } => {
791                let actual = ir.expr(PExpr::Member(member));
792                let expected = ir.expr(PExpr::Int(value));
793                ir.expr(PExpr::Cmp(
794                    if equals { CmpOp::Eq } else { CmpOp::Ne },
795                    actual,
796                    expected,
797                ))
798            }
799        }
800    }
801
802    #[must_use]
803    pub const fn failure_message(self) -> Option<&'static str> {
804        match self {
805            Self::FalseWithMessage { message } => Some(message),
806            Self::True
807            | Self::False
808            | Self::Invoke { .. }
809            | Self::LookaheadTextEquals { .. }
810            | Self::LookaheadNotEquals { .. }
811            | Self::TokenPairAdjacent
812            | Self::ContextChildRuleTextNotEquals { .. }
813            | Self::LocalIntEquals { .. }
814            | Self::LocalIntLessOrEqual { .. }
815            | Self::MemberModuloEquals { .. }
816            | Self::MemberEquals { .. } => None,
817        }
818    }
819}
820
821fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
822    let local = ir.expr(PExpr::LocalArg);
823    let absent = ir.expr(PExpr::IsNull(local));
824    let expected = ir.expr(PExpr::Int(value));
825    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
826    ir.expr(PExpr::Or([absent, comparison].into()))
827}
828
829/// Policy for semantic predicate coordinates that have no runtime
830/// implementation.
831///
832/// ANTLR grammars may embed target-language predicates that the metadata
833/// generator could not translate into a [`ParserPredicate`] table entry. When
834/// recognition reaches such a coordinate the runtime cannot know the grammar
835/// author's intent, so the caller chooses how to proceed.
836///
837/// The default is [`Self::AssumeTrue`], matching the historical behavior of
838/// this runtime. That default is deprecated and will change to [`Self::Error`]
839/// in a future minor release; grammars relying on unconditional predicates
840/// should opt in explicitly.
841#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
842pub enum UnknownSemanticPolicy {
843    /// Treat the predicate as passing, as if it were absent from the grammar.
844    #[default]
845    AssumeTrue,
846    /// Treat the predicate as failing, removing the guarded alternative.
847    AssumeFalse,
848    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
849    /// coordinate that recognition evaluated.
850    Error,
851}
852
853/// Resolves a predicate coordinate that neither a translated table entry nor a
854/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
855///
856/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
857/// so the parse entry can surface every unresolved coordinate afterwards. Both
858/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
859/// funnel through here so a missing implementation is never silently coerced
860/// to a boolean (design goal G1: never silently mis-parse).
861fn apply_unknown_predicate_policy(
862    policy: UnknownSemanticPolicy,
863    rule_index: usize,
864    pred_index: usize,
865    hits: &mut Vec<(usize, usize)>,
866) -> bool {
867    match policy {
868        UnknownSemanticPolicy::AssumeTrue => true,
869        UnknownSemanticPolicy::AssumeFalse => false,
870        UnknownSemanticPolicy::Error => {
871            let coordinate = (rule_index, pred_index);
872            if !hits.contains(&coordinate) {
873                hits.push(coordinate);
874            }
875            false
876        }
877    }
878}
879
880/// Interval-set of expected token types, displayable through a vocabulary —
881/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
882/// generated test actions.
883#[derive(Clone, Debug, Eq, PartialEq)]
884pub struct ExpectedTokenSet {
885    symbols: BTreeSet<i32>,
886}
887
888impl ExpectedTokenSet {
889    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
890    #[must_use]
891    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
892        expected_symbols_display(&self.symbols, vocabulary)
893    }
894}
895
896/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
897///
898/// The first syntax error aborts the parse instead of recovering. Generated
899/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
900#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
901pub struct BailErrorStrategy;
902
903impl BailErrorStrategy {
904    #[must_use]
905    pub const fn new() -> Self {
906        Self
907    }
908}
909
910/// Prediction strategy requested by generated parser harnesses.
911#[derive(Clone, Copy, Debug, Eq, PartialEq)]
912pub enum PredictionMode {
913    /// Prefer the clean full-context outcome when alternatives reach the same
914    /// input position.
915    Ll,
916    /// Preserve SLL's first-viable alternative bias at a decision, even when a
917    /// later full-context alternative could avoid recovery.
918    Sll,
919    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
920    LlExactAmbigDetection,
921}
922
923/// Integer argument metadata for a generated parser rule invocation.
924///
925/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
926/// the generator records the rule-transition source state and the value that
927/// should be visible to semantic predicates inside the callee.
928#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
929pub struct ParserRuleArg {
930    /// ATN state containing the rule transition that receives this argument.
931    pub source_state: usize,
932    /// Callee rule index for the transition.
933    pub rule_index: usize,
934    /// Literal fallback value to expose in the callee.
935    pub value: i64,
936    /// Whether the callee should inherit the caller's current integer argument.
937    pub inherit_local: bool,
938}
939
940/// Integer member mutation attached to an ATN action transition.
941#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
942pub struct ParserMemberAction {
943    /// ATN state containing the action transition.
944    pub source_state: usize,
945    /// Generator-assigned integer member id.
946    pub member: usize,
947    /// Delta applied when the action is reached on one speculative path.
948    pub delta: i64,
949}
950
951/// Integer return-value assignment attached to an ATN action transition.
952///
953/// Generated parsers use this metadata when target actions assign a simple
954/// return field such as `$y=1000;`. The interpreter applies it while selecting
955/// the recognized path so the finished parse tree can answer later
956/// `$label.y` action templates.
957#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
958pub struct ParserReturnAction {
959    /// ATN state containing the action transition.
960    pub source_state: usize,
961    /// Rule index recorded by the serialized action transition.
962    pub rule_index: usize,
963    /// Return-field name as it appears in the grammar.
964    pub name: &'static str,
965    /// Literal integer value assigned by the action.
966    pub value: i64,
967}
968
969impl ParserMemberAction {
970    /// Lowers this speculative member mutation into a `SemIR` action.
971    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
972        let delta = ir.expr(PExpr::Int(self.delta));
973        ParserSemanticAction {
974            source_state: self.source_state,
975            rule_index: usize::MAX,
976            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
977            speculative: true,
978        }
979    }
980}
981
982impl ParserReturnAction {
983    /// Lowers this committed return-value assignment into a `SemIR` action.
984    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
985        let name = ir.intern(self.name);
986        let value = ir.expr(PExpr::Int(self.value));
987        ParserSemanticAction {
988            source_state: self.source_state,
989            rule_index: self.rule_index,
990            stmt: ir.stmt(AStmt::SetReturn(name, value)),
991            speculative: false,
992        }
993    }
994}
995
996/// Parser predicate coordinate lowered into [`SemIr`].
997#[derive(Clone, Copy, Debug, Eq, PartialEq)]
998pub struct ParserSemanticPredicate {
999    /// Serialized rule index that owns this predicate.
1000    pub rule_index: usize,
1001    /// Predicate index inside the owning rule.
1002    pub pred_index: usize,
1003    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1004    pub expr: ExprId,
1005    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1006    pub failure_message: Option<&'static str>,
1007}
1008
1009/// Parser action coordinate lowered into [`SemIr`].
1010#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1011pub struct ParserSemanticAction {
1012    /// ATN state containing the action transition.
1013    pub source_state: usize,
1014    /// Serialized rule index recorded by the action transition.
1015    pub rule_index: usize,
1016    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1017    pub stmt: StmtId,
1018    /// Whether this action may run on speculative recognition paths.
1019    pub speculative: bool,
1020}
1021
1022/// Data-driven semantic tables emitted by generated parsers.
1023///
1024/// This is the runtime representation for issue #9's `SemIR` path. Existing
1025/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1026/// remain accepted as deprecated adapters for generated code produced before
1027/// this table existed.
1028#[derive(Clone, Debug, Default, Eq, PartialEq)]
1029pub struct ParserSemantics {
1030    pub ir: SemIr,
1031    pub predicates: Vec<ParserSemanticPredicate>,
1032    pub actions: Vec<ParserSemanticAction>,
1033}
1034
1035/// Optional generated-runtime metadata for metadata-driven parser execution.
1036#[derive(Clone, Copy, Debug, Default)]
1037pub struct ParserRuntimeOptions<'a> {
1038    /// Rule indexes whose `@init` actions should be replayed.
1039    pub init_action_rules: &'a [usize],
1040    /// Whether generated parse-tree contexts should retain alternative numbers.
1041    pub track_alt_numbers: bool,
1042    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1043    pub predicates: &'a [(usize, usize, ParserPredicate)],
1044    /// `SemIR` predicate/action table emitted by newer generated parsers.
1045    pub semantics: Option<&'a ParserSemantics>,
1046    /// Rule-call integer argument table keyed by ATN source state.
1047    pub rule_args: &'a [ParserRuleArg],
1048    /// Integer member mutations keyed by ATN action source state.
1049    pub member_actions: &'a [ParserMemberAction],
1050    /// Integer return assignments keyed by ATN action source state.
1051    pub return_actions: &'a [ParserReturnAction],
1052    /// How to evaluate semantic predicate coordinates absent from
1053    /// `predicates`.
1054    pub unknown_predicate_policy: UnknownSemanticPolicy,
1055}
1056
1057pub trait Parser: Recognizer {
1058    /// Reports whether generated parser rules should build parse-tree nodes
1059    /// while recognizing input.
1060    fn build_parse_trees(&self) -> bool;
1061
1062    /// Enables or disables parse-tree construction for subsequent rule calls.
1063    fn set_build_parse_trees(&mut self, build: bool);
1064
1065    /// Returns the number of parser syntax errors recorded by committed parse
1066    /// paths so far.
1067    fn number_of_syntax_errors(&self) -> usize {
1068        0
1069    }
1070
1071    /// Reports whether prediction diagnostic-listener messages are emitted
1072    /// during parser ATN recognition.
1073    fn report_diagnostic_errors(&self) -> bool {
1074        false
1075    }
1076
1077    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1078    /// rule calls.
1079    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1080
1081    /// Reports the prediction strategy used when selecting among alternatives.
1082    fn prediction_mode(&self) -> PredictionMode {
1083        PredictionMode::Ll
1084    }
1085
1086    /// Sets the prediction strategy for subsequent rule calls.
1087    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1088}
1089
1090#[derive(Debug)]
1091struct LeftRecursiveCallerOverlap {
1092    atn_key: SharedAtnCacheKey,
1093    state_number: usize,
1094    symbol: i32,
1095    context_version: usize,
1096    overlaps: bool,
1097}
1098
1099const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1100
1101#[derive(Debug)]
1102pub struct BaseParser<S, H = NoSemanticHooks> {
1103    input: CommonTokenStream<S>,
1104    tree: ParseTreeStorage,
1105    data: RecognizerData,
1106    semantic_hooks: H,
1107    build_parse_trees: bool,
1108    syntax_errors: usize,
1109    report_diagnostic_errors: bool,
1110    prediction_mode: PredictionMode,
1111    prediction_diagnostics: Vec<ParserDiagnostic>,
1112    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1113    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1114    generated_sync_expected: Option<TokenBitSet>,
1115    int_members: BTreeMap<usize, i64>,
1116    rule_context_stack: Vec<RuleContextFrame>,
1117    rule_context_version: usize,
1118    left_recursive_caller_overlap_cache:
1119        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1120    pending_invoking_states: Vec<isize>,
1121    precedence_stack: Vec<i32>,
1122    /// Predicate side effects are observable in a few target-template tests;
1123    /// speculative recognition may revisit the same coordinate, so replay it
1124    /// once per parser instance.
1125    invoked_predicates: Vec<(usize, usize)>,
1126    /// Bail error strategy: the first syntax error aborts the parse instead of
1127    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1128    /// through `set_error_handler(BailErrorStrategy::new())`.
1129    bail_on_error: bool,
1130    /// How to evaluate predicate coordinates missing from the active
1131    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1132    unknown_predicate_policy: UnknownSemanticPolicy,
1133    /// Unknown predicate coordinates evaluated by the current parse, recorded
1134    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1135    unknown_predicate_hits: Vec<(usize, usize)>,
1136    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1137    /// that no hook handled, recorded so a generated `hook`/error-disposed
1138    /// action fails loud instead of being silently dropped. Keyed by
1139    /// `(rule_index, source_state)`.
1140    unhandled_action_hits: Vec<(usize, usize)>,
1141    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1142    /// hot rule-transition checks to a vector lookup after the first visit
1143    /// while the thread-local shared ATN cache still owns the cross-parse
1144    /// computed value.
1145    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1146    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1147    /// epsilon-reachable consuming transition and shows up as a hot loop in
1148    /// `next_recovery_context` and recovery diagnostics on long inputs.
1149    /// Keying on `state_number` and sharing the result through `Rc` removes
1150    /// repeated DFS plus per-call `BTreeSet` allocations.
1151    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1152    /// Same expected-symbol cache as a bitset for generated parser sync.
1153    /// Successful parses only need `contains` and union; keeping that path out
1154    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1155    /// check and defers deterministic formatting to diagnostics.
1156    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1157    /// Per-state cache for whether a return state can finish its owning rule
1158    /// without consuming more input. Generated-parser sync uses this to walk
1159    /// parent prediction contexts for nullable exits without paying repeated
1160    /// epsilon-closure searches on every loop or optional decision.
1161    rule_stop_reach_cache: Vec<Option<bool>>,
1162    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1163    /// threads the same epsilon-recovery context through hundreds of follow
1164    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1165    /// reference bump and lets the memo key hash by pointer.
1166    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1167    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1168    /// touch a given decision state still pay no upfront cost; once cached,
1169    /// the recognizer prunes alternatives whose look-1 cannot accept the
1170    /// current lookahead, letting common SLL decisions reduce to a single
1171    /// transition walk instead of a full speculative fan-out.
1172    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1173    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1174    /// Each multi-trans visit asks "given this decision state and this
1175    /// lookahead token, which alt do I commit to?" Hitting this cache
1176    /// turns the question into a hashmap probe instead of re-scanning
1177    /// the decision's per-transition FIRST sets every visit.
1178    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1179    /// Predicate results shared by the fast recognizer's clean and recovery
1180    /// attempts. The eligible fast path keeps every runtime-provided input
1181    /// fixed, and custom predicate hooks are required to be replay-safe.
1182    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1183    /// Cache for whether an ATN state can reach itself without consuming
1184    /// input. Only those states need the recursive recognizer's
1185    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1186    /// grammar-static cache survive parser resets without reusing state
1187    /// coordinates after the parser is driven against a different ATN.
1188    empty_cycle_cache: Vec<Option<bool>>,
1189    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1190    /// Probe state for deciding whether clean-pass memo entries are worth
1191    /// storing for the current parse.
1192    clean_memo_mode: CleanMemoMode,
1193    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1194    clean_memo_probe_samples: usize,
1195    clean_memo_probe_repeats: usize,
1196    clean_memo_sparse_samples: usize,
1197    /// Reusable cycle and memo storage for one top-level fast recognition.
1198    fast_recognize_scratch: FastRecognizeTopScratch,
1199    /// Reusable direct-index/hash storage for clean speculative endpoints.
1200    fast_outcome_dedup: FastOutcomeDedupScratch,
1201    /// Empty recovery-symbols singleton used as the default at rule entry and
1202    /// after token consumption.
1203    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1204    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1205    /// prefilter trims speculative rule calls whose called rule cannot
1206    /// match the current lookahead, but it also bypasses single-token
1207    /// insertion / deletion recovery that ANTLR runs at the rule's first
1208    /// consuming transition. `parse_atn_rule` flips this off and retries
1209    /// when the first pass produces no clean outcome so the runtime can
1210    /// repair inputs the reference parser would have repaired.
1211    fast_first_set_prefilter: bool,
1212    /// Whether the fast recognizer should explore parser error-recovery paths.
1213    /// Public rule parsing starts with this disabled for the common valid-input
1214    /// path and enables it only for the retry that needs ANTLR-style repairs.
1215    fast_recovery_enabled: bool,
1216    /// Whether the fast recognizer should record terminal-token nodes while
1217    /// speculating. Clean valid-input parsing can reconstruct terminals from
1218    /// selected rule spans after recognition, avoiding many speculative
1219    /// nodes that are thrown away with losing paths.
1220    fast_token_nodes_enabled: bool,
1221    /// Parser-owned append-only storage for speculative recognition output.
1222    /// Each public interpreted-rule entry clears lengths while retaining
1223    /// bounded backing capacities for parser reuse.
1224    recognition_arena: RecognitionArena,
1225    last_recognition_arena_root: NodeSeqId,
1226    last_recognition_arena_diagnostics: DiagnosticSeqId,
1227}
1228
1229/// Rollback marker for speculative generated parser paths.
1230#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1231pub struct GeneratedDiagnosticsCheckpoint {
1232    diagnostics_len: usize,
1233    syntax_errors: usize,
1234    tree: ParseTreeCheckpoint,
1235}
1236
1237/// Storage and reachability counters for the most recent interpreted-rule
1238/// recognition arena.
1239#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1240pub struct RecognitionArenaStats {
1241    pub total_nodes: usize,
1242    pub live_nodes: usize,
1243    pub dead_nodes: usize,
1244    pub node_capacity: usize,
1245    pub total_links: usize,
1246    pub live_links: usize,
1247    pub dead_links: usize,
1248    pub link_capacity: usize,
1249    pub total_extras: usize,
1250    pub live_extras: usize,
1251    pub dead_extras: usize,
1252    pub extra_capacity: usize,
1253}
1254
1255#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1256struct RuleContextFrame {
1257    rule_index: usize,
1258    invoking_state: isize,
1259}
1260
1261#[derive(Clone, Debug, Eq, PartialEq)]
1262struct RecognizeOutcome {
1263    index: usize,
1264    consumed_eof: bool,
1265    alt_number: usize,
1266    member_values: BTreeMap<usize, i64>,
1267    return_values: BTreeMap<String, i64>,
1268    diagnostics: DiagnosticSeqId,
1269    decisions: Vec<usize>,
1270    actions: Vec<ParserAction>,
1271    nodes: NodeSeqId,
1272}
1273
1274#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1275struct FastRecognizeOutcome {
1276    index: usize,
1277    consumed_eof: bool,
1278    diagnostics: DiagnosticSeqId,
1279    deferred_nodes: FastDeferredNodeId,
1280    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1281    /// Copying an outcome copies this compact ID; prepending appends one
1282    /// `SeqLink` without allocating an individual node or list tail.
1283    nodes: NodeSeqId,
1284}
1285
1286#[derive(Debug, Default)]
1287struct FastRecognizeTopScratch {
1288    visiting: FxHashSet<FastRecognizeKey>,
1289    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1290}
1291
1292impl FastRecognizeTopScratch {
1293    fn prepare(&mut self, memo_capacity: usize) {
1294        self.visiting.clear();
1295        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1296        self.memo.clear();
1297        self.memo.reserve(memo_capacity);
1298    }
1299
1300    fn release_oversized_memo(&mut self) {
1301        self.memo.clear();
1302        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1303            self.memo = FxHashMap::default();
1304        }
1305    }
1306}
1307
1308fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1309    buffered_tokens.saturating_mul(8).clamp(
1310        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1311        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1312    )
1313}
1314
1315#[derive(Debug, Default)]
1316struct FastOutcomeDedupScratch {
1317    dense_words: Vec<u64>,
1318    touched_dense_words: Vec<u32>,
1319    sparse_keys: FxHashSet<(usize, bool)>,
1320}
1321
1322/// Handle into the parser-owned deferred tree rope.
1323///
1324/// The sentinel keeps outcomes and repetition paths compact without an
1325/// `Option` discriminant or per-node reference counting.
1326#[repr(transparent)]
1327#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1328struct FastDeferredNodeId(u32);
1329
1330impl FastDeferredNodeId {
1331    const EMPTY: Self = Self(u32::MAX);
1332
1333    const fn is_empty(self) -> bool {
1334        self.0 == Self::EMPTY.0
1335    }
1336}
1337
1338impl Default for FastDeferredNodeId {
1339    fn default() -> Self {
1340        Self::EMPTY
1341    }
1342}
1343
1344#[repr(transparent)]
1345#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1346struct FastDeferredRuleId(u32);
1347
1348/// One immutable deferred-tree rope record in `RecognitionArena`.
1349#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1350enum FastDeferredNode {
1351    Fragment(NodeSeqId),
1352    Rule(FastDeferredRuleId),
1353    Concat {
1354        prefix: FastDeferredNodeId,
1355        suffix: FastDeferredNodeId,
1356    },
1357}
1358
1359#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1360struct FastDeferredRule {
1361    rule_index: u32,
1362    invoking_state: i32,
1363    start_index: u32,
1364    stop_index: Option<u32>,
1365    deferred_children: FastDeferredNodeId,
1366    children: NodeSeqId,
1367}
1368
1369#[repr(transparent)]
1370#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1371struct RecognizedNodeId(u32);
1372
1373#[repr(transparent)]
1374#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1375struct NodeSeqId(u32);
1376
1377impl NodeSeqId {
1378    const EMPTY: Self = Self(u32::MAX);
1379
1380    const fn is_empty(self) -> bool {
1381        self.0 == Self::EMPTY.0
1382    }
1383}
1384
1385impl Default for NodeSeqId {
1386    fn default() -> Self {
1387        Self::EMPTY
1388    }
1389}
1390
1391#[repr(transparent)]
1392#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1393struct DiagnosticSeqId(u32);
1394
1395impl DiagnosticSeqId {
1396    const EMPTY: Self = Self(u32::MAX);
1397
1398    const fn is_empty(self) -> bool {
1399        self.0 == Self::EMPTY.0
1400    }
1401}
1402
1403impl Default for DiagnosticSeqId {
1404    fn default() -> Self {
1405        Self::EMPTY
1406    }
1407}
1408
1409#[repr(transparent)]
1410#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1411struct RecognitionExtraId(u32);
1412
1413#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1414struct SeqLink {
1415    head: RecognizedNodeId,
1416    tail: NodeSeqId,
1417}
1418
1419#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1420struct DiagnosticLink {
1421    head: RecognitionExtraId,
1422    tail: DiagnosticSeqId,
1423}
1424
1425struct ArenaRuleSpec {
1426    rule_index: usize,
1427    invoking_state: isize,
1428    alt_number: usize,
1429    start_index: usize,
1430    stop_index: Option<usize>,
1431    return_values: BTreeMap<String, i64>,
1432    children: NodeSeqId,
1433}
1434
1435/// Compact speculative node record. Common records contain only IDs and
1436/// scalars; missing-token text and generated return values live in `extras`.
1437#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1438enum ArenaRecognizedNode {
1439    Token {
1440        token: TokenId,
1441    },
1442    ErrorToken {
1443        token: TokenId,
1444    },
1445    MissingToken {
1446        extra: RecognitionExtraId,
1447    },
1448    Rule {
1449        rule_index: u32,
1450        invoking_state: i32,
1451        alt_number: u32,
1452        start_index: u32,
1453        stop_index: Option<u32>,
1454        return_values: Option<RecognitionExtraId>,
1455        children: NodeSeqId,
1456    },
1457    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1458    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1459    /// public rule entry hands the tree to the caller.
1460    LeftRecursiveBoundary {
1461        rule_index: u32,
1462    },
1463}
1464
1465#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1466enum RecognitionExtra {
1467    MissingToken {
1468        token_type: i32,
1469        at_index: u32,
1470        text: String,
1471    },
1472    ReturnValues(BTreeMap<String, i64>),
1473    Diagnostic(ParserDiagnostic),
1474}
1475
1476#[derive(Debug, Default)]
1477struct RecognitionArena {
1478    nodes: Vec<ArenaRecognizedNode>,
1479    seq_links: Vec<SeqLink>,
1480    diagnostic_links: Vec<DiagnosticLink>,
1481    extras: Vec<RecognitionExtra>,
1482    deferred_nodes: Vec<FastDeferredNode>,
1483    deferred_rules: Vec<FastDeferredRule>,
1484}
1485
1486// Preserve normal parser reuse while preventing one pathological parse from
1487// pinning an arbitrarily large arena for the parser's remaining lifetime.
1488const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1489const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1490const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1491const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1492const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1493const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1494
1495impl RecognitionArena {
1496    fn reset(&mut self) {
1497        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1498        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1499        reset_arena_vec(
1500            &mut self.diagnostic_links,
1501            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1502        );
1503        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1504        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1505        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1506    }
1507
1508    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1509        let id = RecognizedNodeId(
1510            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1511        );
1512        self.nodes.push(node);
1513        id
1514    }
1515
1516    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1517        let id = RecognitionExtraId(
1518            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1519        );
1520        self.extras.push(extra);
1521        id
1522    }
1523
1524    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1525        let id = NodeSeqId(
1526            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1527        );
1528        self.seq_links.push(SeqLink { head, tail });
1529        id
1530    }
1531
1532    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1533        let id = FastDeferredNodeId(
1534            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1535        );
1536        self.deferred_nodes.push(node);
1537        id
1538    }
1539
1540    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1541        let id = FastDeferredRuleId(
1542            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1543        );
1544        self.deferred_rules.push(rule);
1545        id
1546    }
1547
1548    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1549        if nodes.is_empty() {
1550            FastDeferredNodeId::EMPTY
1551        } else {
1552            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1553        }
1554    }
1555
1556    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1557        let rule = self.push_deferred_rule(rule);
1558        self.push_deferred_node(FastDeferredNode::Rule(rule))
1559    }
1560
1561    fn concat_deferred_nodes(
1562        &mut self,
1563        prefix: FastDeferredNodeId,
1564        suffix: FastDeferredNodeId,
1565    ) -> FastDeferredNodeId {
1566        if prefix.is_empty() {
1567            return suffix;
1568        }
1569        if suffix.is_empty() {
1570            return prefix;
1571        }
1572        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1573    }
1574
1575    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1576        self.deferred_nodes[id.0 as usize]
1577    }
1578
1579    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1580        self.deferred_rules[id.0 as usize]
1581    }
1582
1583    fn prepend_diagnostic(
1584        &mut self,
1585        tail: DiagnosticSeqId,
1586        diagnostic: ParserDiagnostic,
1587    ) -> DiagnosticSeqId {
1588        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1589        self.prepend_diagnostic_id(tail, head)
1590    }
1591
1592    fn prepend_diagnostic_id(
1593        &mut self,
1594        tail: DiagnosticSeqId,
1595        head: RecognitionExtraId,
1596    ) -> DiagnosticSeqId {
1597        let id = DiagnosticSeqId(
1598            u32::try_from(self.diagnostic_links.len())
1599                .expect("diagnostic sequence arena fits in u32"),
1600        );
1601        self.diagnostic_links.push(DiagnosticLink { head, tail });
1602        id
1603    }
1604
1605    fn concat_diagnostics(
1606        &mut self,
1607        prefix: DiagnosticSeqId,
1608        mut suffix: DiagnosticSeqId,
1609    ) -> DiagnosticSeqId {
1610        if prefix.is_empty() {
1611            return suffix;
1612        }
1613        if suffix.is_empty() {
1614            return prefix;
1615        }
1616        let mut reversed = DiagnosticSeqId::EMPTY;
1617        let mut cursor = prefix;
1618        while let Some(link) = self.diagnostic_link(cursor) {
1619            reversed = self.prepend_diagnostic_id(reversed, link.head);
1620            cursor = link.tail;
1621        }
1622        while let Some(link) = self.diagnostic_link(reversed) {
1623            suffix = self.prepend_diagnostic_id(suffix, link.head);
1624            reversed = link.tail;
1625        }
1626        suffix
1627    }
1628
1629    #[cfg(test)]
1630    fn diagnostic_sequence(
1631        &mut self,
1632        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1633    ) -> DiagnosticSeqId {
1634        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1635        let mut sequence = DiagnosticSeqId::EMPTY;
1636        for diagnostic in diagnostics.into_iter().rev() {
1637            sequence = self.prepend_diagnostic(sequence, diagnostic);
1638        }
1639        sequence
1640    }
1641
1642    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1643        self.nodes[id.0 as usize]
1644    }
1645
1646    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1647        &self.extras[id.0 as usize]
1648    }
1649
1650    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1651        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1652    }
1653
1654    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1655        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1656    }
1657
1658    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1659        NodeSeqIter {
1660            arena: self,
1661            cursor: sequence,
1662        }
1663    }
1664
1665    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1666        DiagnosticSeqIter {
1667            arena: self,
1668            cursor: sequence,
1669        }
1670    }
1671
1672    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1673        self.diagnostics(sequence).count()
1674    }
1675
1676    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1677        self.diagnostics(sequence)
1678            .filter(|diagnostic| {
1679                diagnostic.message.starts_with("mismatched input ")
1680                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1681            })
1682            .count()
1683    }
1684
1685    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1686        self.diagnostics(left).cmp(self.diagnostics(right))
1687    }
1688
1689    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1690        self.iter(sequence).count()
1691    }
1692
1693    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1694        self.iter(sequence).any(|node| match self.node(node) {
1695            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1696            ArenaRecognizedNode::Rule { children, .. } => {
1697                self.sequence_has_left_recursive_boundary(children)
1698            }
1699            ArenaRecognizedNode::Token { .. }
1700            | ArenaRecognizedNode::ErrorToken { .. }
1701            | ArenaRecognizedNode::MissingToken { .. } => false,
1702        })
1703    }
1704
1705    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1706        self.iter(sequence).any(|node| {
1707            matches!(
1708                self.node(node),
1709                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1710            )
1711        })
1712    }
1713
1714    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1715        self.iter(sequence).any(|node| {
1716            matches!(
1717                self.node(node),
1718                ArenaRecognizedNode::Token { .. }
1719                    | ArenaRecognizedNode::ErrorToken { .. }
1720                    | ArenaRecognizedNode::MissingToken { .. }
1721            )
1722        })
1723    }
1724
1725    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1726        match self.node(node) {
1727            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1728                Some(token.index())
1729            }
1730            ArenaRecognizedNode::MissingToken { extra } => {
1731                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1732                    unreachable!("missing-token node must reference missing-token extra");
1733                };
1734                Some(*at_index as usize)
1735            }
1736            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1737            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1738        }
1739    }
1740
1741    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
1742        match self.node(node) {
1743            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1744                Some(token.index())
1745            }
1746            ArenaRecognizedNode::MissingToken { extra } => {
1747                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1748                    unreachable!("missing-token node must reference missing-token extra");
1749                };
1750                (*at_index as usize).checked_sub(1)
1751            }
1752            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
1753            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1754        }
1755    }
1756
1757    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
1758        let start = self.node_start_index(node)?;
1759        let stop = self.node_stop_index(node);
1760        Some((start, stop))
1761    }
1762
1763    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
1764        self.iter(sequence)
1765            .find_map(|node| self.node_start_index(node))
1766    }
1767
1768    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
1769        let mut stop = None;
1770        for node in self.iter(sequence) {
1771            if let Some(index) = self.node_stop_index(node) {
1772                stop = Some(index);
1773            }
1774        }
1775        stop
1776    }
1777
1778    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
1779        self.iter(sequence)
1780            .any(|node| self.node_needs_stable_tie(node))
1781    }
1782
1783    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
1784        match self.node(node) {
1785            ArenaRecognizedNode::Token { .. }
1786            | ArenaRecognizedNode::ErrorToken { .. }
1787            | ArenaRecognizedNode::MissingToken { .. } => false,
1788            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1789            ArenaRecognizedNode::Rule {
1790                rule_index,
1791                children,
1792                ..
1793            } => self.iter(children).any(|child| {
1794                matches!(
1795                    self.node(child),
1796                    ArenaRecognizedNode::Rule {
1797                        rule_index: child_rule,
1798                        ..
1799                    } if child_rule == rule_index
1800                ) || self.node_needs_stable_tie(child)
1801            }),
1802        }
1803    }
1804
1805    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
1806        loop {
1807            match (self.link(left), self.link(right)) {
1808                (Some(left_link), Some(right_link)) => {
1809                    let order = self.compare_nodes(left_link.head, right_link.head);
1810                    if order != Ordering::Equal {
1811                        return order;
1812                    }
1813                    left = left_link.tail;
1814                    right = right_link.tail;
1815                }
1816                (None, None) => return Ordering::Equal,
1817                (None, Some(_)) => return Ordering::Less,
1818                (Some(_), None) => return Ordering::Greater,
1819            }
1820        }
1821    }
1822
1823    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
1824        let left = self.node(left);
1825        let right = self.node(right);
1826        match (left, right) {
1827            (
1828                ArenaRecognizedNode::Token { token: left },
1829                ArenaRecognizedNode::Token { token: right },
1830            )
1831            | (
1832                ArenaRecognizedNode::ErrorToken { token: left },
1833                ArenaRecognizedNode::ErrorToken { token: right },
1834            ) => left.cmp(&right),
1835            (
1836                ArenaRecognizedNode::MissingToken { extra: left },
1837                ArenaRecognizedNode::MissingToken { extra: right },
1838            ) => self.extra(left).cmp(self.extra(right)),
1839            (
1840                ArenaRecognizedNode::Rule {
1841                    rule_index: left_rule,
1842                    invoking_state: left_invoking,
1843                    alt_number: left_alt,
1844                    start_index: left_start,
1845                    stop_index: left_stop,
1846                    return_values: left_returns,
1847                    children: left_children,
1848                },
1849                ArenaRecognizedNode::Rule {
1850                    rule_index: right_rule,
1851                    invoking_state: right_invoking,
1852                    alt_number: right_alt,
1853                    start_index: right_start,
1854                    stop_index: right_stop,
1855                    return_values: right_returns,
1856                    children: right_children,
1857                },
1858            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
1859                .cmp(&(
1860                    right_rule,
1861                    right_invoking,
1862                    right_alt,
1863                    right_start,
1864                    right_stop,
1865                ))
1866                .then_with(|| {
1867                    left_returns
1868                        .map(|id| self.extra(id))
1869                        .cmp(&right_returns.map(|id| self.extra(id)))
1870                })
1871                .then_with(|| self.compare_sequences(left_children, right_children)),
1872            (
1873                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: left },
1874                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: right },
1875            ) => left.cmp(&right),
1876            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
1877        }
1878    }
1879
1880    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1881        let mut reversed = NodeSeqId::EMPTY;
1882        while let Some(link) = self.link(sequence) {
1883            reversed = self.prepend(reversed, link.head);
1884            sequence = link.tail;
1885        }
1886        reversed
1887    }
1888
1889    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1890        if !self.sequence_has_direct_boundary(sequence) {
1891            return sequence;
1892        }
1893        let mut reversed = NodeSeqId::EMPTY;
1894        while let Some(link) = self.link(sequence) {
1895            match self.node(link.head) {
1896                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
1897                    if !reversed.is_empty() {
1898                        let children = self.reverse_sequence(reversed);
1899                        let start_index = self.sequence_start_index(children).unwrap_or_default();
1900                        let stop_index = self.sequence_stop_index(children);
1901                        let rule = self.push_node(ArenaRecognizedNode::Rule {
1902                            rule_index,
1903                            invoking_state: -1,
1904                            alt_number: 0,
1905                            start_index: u32::try_from(start_index)
1906                                .expect("left-recursive start index fits in u32"),
1907                            stop_index: stop_index.map(|index| {
1908                                u32::try_from(index).expect("left-recursive stop index fits in u32")
1909                            }),
1910                            return_values: None,
1911                            children,
1912                        });
1913                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
1914                    }
1915                }
1916                _ => {
1917                    reversed = self.prepend(reversed, link.head);
1918                }
1919            }
1920            sequence = link.tail;
1921        }
1922        self.reverse_sequence(reversed)
1923    }
1924
1925    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
1926        let mut live_nodes = vec![false; self.nodes.len()];
1927        let mut live_links = vec![false; self.seq_links.len()];
1928        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
1929        let mut live_extras = vec![false; self.extras.len()];
1930        let mut pending = vec![root];
1931        while let Some(mut sequence) = pending.pop() {
1932            while let Some(link) = self.link(sequence) {
1933                let link_index = sequence.0 as usize;
1934                if live_links[link_index] {
1935                    break;
1936                }
1937                live_links[link_index] = true;
1938                let node_index = link.head.0 as usize;
1939                if !live_nodes[node_index] {
1940                    live_nodes[node_index] = true;
1941                    match self.node(link.head) {
1942                        ArenaRecognizedNode::MissingToken { extra } => {
1943                            live_extras[extra.0 as usize] = true;
1944                        }
1945                        ArenaRecognizedNode::Rule {
1946                            return_values,
1947                            children,
1948                            ..
1949                        } => {
1950                            if let Some(extra) = return_values {
1951                                live_extras[extra.0 as usize] = true;
1952                            }
1953                            pending.push(children);
1954                        }
1955                        ArenaRecognizedNode::Token { .. }
1956                        | ArenaRecognizedNode::ErrorToken { .. }
1957                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
1958                    }
1959                }
1960                sequence = link.tail;
1961            }
1962        }
1963        let mut diagnostics = diagnostics;
1964        while let Some(link) = self.diagnostic_link(diagnostics) {
1965            let link_index = diagnostics.0 as usize;
1966            if live_diagnostic_links[link_index] {
1967                break;
1968            }
1969            live_diagnostic_links[link_index] = true;
1970            live_extras[link.head.0 as usize] = true;
1971            diagnostics = link.tail;
1972        }
1973        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
1974        let live_link_count = live_links.into_iter().filter(|live| *live).count()
1975            + live_diagnostic_links
1976                .into_iter()
1977                .filter(|live| *live)
1978                .count();
1979        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
1980        let total_links = self.seq_links.len() + self.diagnostic_links.len();
1981        RecognitionArenaStats {
1982            total_nodes: self.nodes.len(),
1983            live_nodes: live_node_count,
1984            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
1985            node_capacity: self.nodes.capacity(),
1986            total_links,
1987            live_links: live_link_count,
1988            dead_links: total_links.saturating_sub(live_link_count),
1989            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
1990            total_extras: self.extras.len(),
1991            live_extras: live_extra_count,
1992            dead_extras: self.extras.len().saturating_sub(live_extra_count),
1993            extra_capacity: self.extras.capacity(),
1994        }
1995    }
1996}
1997
1998fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
1999    if storage.capacity() > max_retained_capacity {
2000        *storage = Vec::new();
2001    } else {
2002        storage.clear();
2003    }
2004}
2005
2006const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2007    match node {
2008        ArenaRecognizedNode::Token { .. } => 0,
2009        ArenaRecognizedNode::ErrorToken { .. } => 1,
2010        ArenaRecognizedNode::MissingToken { .. } => 2,
2011        ArenaRecognizedNode::Rule { .. } => 3,
2012        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2013    }
2014}
2015
2016struct NodeSeqIter<'a> {
2017    arena: &'a RecognitionArena,
2018    cursor: NodeSeqId,
2019}
2020
2021impl Iterator for NodeSeqIter<'_> {
2022    type Item = RecognizedNodeId;
2023
2024    fn next(&mut self) -> Option<Self::Item> {
2025        let link = self.arena.link(self.cursor)?;
2026        self.cursor = link.tail;
2027        Some(link.head)
2028    }
2029}
2030
2031struct DiagnosticSeqIter<'a> {
2032    arena: &'a RecognitionArena,
2033    cursor: DiagnosticSeqId,
2034}
2035
2036impl<'a> Iterator for DiagnosticSeqIter<'a> {
2037    type Item = &'a ParserDiagnostic;
2038
2039    fn next(&mut self) -> Option<Self::Item> {
2040        let link = self.arena.diagnostic_link(self.cursor)?;
2041        self.cursor = link.tail;
2042        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2043            unreachable!("diagnostic link must reference diagnostic extra");
2044        };
2045        Some(diagnostic)
2046    }
2047}
2048
2049#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2050struct ParserDiagnostic {
2051    line: usize,
2052    column: usize,
2053    message: String,
2054}
2055
2056#[derive(Clone, Debug, Default, Eq, PartialEq)]
2057struct ExpectedTokens {
2058    index: Option<usize>,
2059    symbols: BTreeSet<i32>,
2060    no_viable: Option<NoViableAlternative>,
2061}
2062
2063#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2064struct NoViableAlternative {
2065    start_index: usize,
2066    error_index: usize,
2067}
2068
2069impl ExpectedTokens {
2070    /// Records the expected symbols for the farthest token index reached by any
2071    /// failed ATN path.
2072    fn record_transition(
2073        &mut self,
2074        index: usize,
2075        transition: ParserTransition<'_>,
2076        max_token_type: i32,
2077    ) {
2078        let symbols = transition_expected_symbols(transition, max_token_type);
2079        match self.index {
2080            Some(current) if index < current => {}
2081            Some(current) if index == current => self.symbols.extend(symbols),
2082            _ => {
2083                self.index = Some(index);
2084                self.symbols = symbols;
2085            }
2086        }
2087    }
2088
2089    /// Records an ambiguous decision that failed after consuming a shared
2090    /// prefix, which ANTLR reports as `no viable alternative`.
2091    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2092        match self.no_viable {
2093            Some(current) if error_index < current.error_index => {}
2094            _ => {
2095                self.no_viable = Some(NoViableAlternative {
2096                    start_index,
2097                    error_index,
2098                });
2099            }
2100        }
2101    }
2102}
2103
2104/// Compact token-type set for parser-internal FIRST/lookahead caches.
2105///
2106/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2107/// but the hot recognizer path mostly needs `contains` and set union over
2108/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2109/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2110#[derive(Clone, Debug, Default, Eq, PartialEq)]
2111struct TokenBitSet {
2112    words: Vec<u64>,
2113}
2114
2115impl TokenBitSet {
2116    fn insert(&mut self, symbol: i32) {
2117        let Some(slot) = token_bit_slot(symbol) else {
2118            return;
2119        };
2120        let word = slot / u64::BITS as usize;
2121        if word >= self.words.len() {
2122            self.words.resize(word + 1, 0);
2123        }
2124        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2125    }
2126
2127    fn extend_range(&mut self, start: i32, stop: i32) {
2128        let (start, stop) = if start <= stop {
2129            (start, stop)
2130        } else {
2131            (stop, start)
2132        };
2133        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2134            self.insert(TOKEN_EOF);
2135        }
2136        let positive_start = start.max(1);
2137        if positive_start > stop {
2138            return;
2139        }
2140        let Some(start_slot) = token_bit_slot(positive_start) else {
2141            return;
2142        };
2143        let Some(stop_slot) = token_bit_slot(stop) else {
2144            return;
2145        };
2146        self.extend_slot_range(start_slot, stop_slot);
2147    }
2148
2149    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2150        if start_slot > stop_slot {
2151            return;
2152        }
2153        let start_word = start_slot / u64::BITS as usize;
2154        let stop_word = stop_slot / u64::BITS as usize;
2155        if stop_word >= self.words.len() {
2156            self.words.resize(stop_word + 1, 0);
2157        }
2158        let start_offset = start_slot % u64::BITS as usize;
2159        let stop_offset = stop_slot % u64::BITS as usize;
2160        if start_word == stop_word {
2161            self.words[start_word] |=
2162                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2163            return;
2164        }
2165        self.words[start_word] |= !0_u64 << start_offset;
2166        for word in &mut self.words[(start_word + 1)..stop_word] {
2167            *word = !0_u64;
2168        }
2169        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2170    }
2171
2172    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2173        for symbol in symbols {
2174            self.insert(symbol);
2175        }
2176    }
2177
2178    fn extend_from(&mut self, other: &Self) {
2179        if other.words.len() > self.words.len() {
2180            self.words.resize(other.words.len(), 0);
2181        }
2182        for (left, right) in self.words.iter_mut().zip(&other.words) {
2183            *left |= *right;
2184        }
2185    }
2186
2187    fn contains(&self, symbol: i32) -> bool {
2188        let Some(slot) = token_bit_slot(symbol) else {
2189            return false;
2190        };
2191        let word = slot / u64::BITS as usize;
2192        self.words
2193            .get(word)
2194            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2195    }
2196
2197    fn is_empty(&self) -> bool {
2198        self.words.iter().all(|word| *word == 0)
2199    }
2200
2201    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2202        for (word_index, word) in self.words.iter().copied().enumerate() {
2203            let mut bits = word;
2204            while bits != 0 {
2205                let bit = bits.trailing_zeros() as usize;
2206                if let Some(symbol) = token_bit_symbol(word_index * u64::BITS as usize + bit) {
2207                    target.insert(symbol);
2208                }
2209                bits &= bits - 1;
2210            }
2211        }
2212    }
2213
2214    fn to_btree_set(&self) -> BTreeSet<i32> {
2215        let mut out = BTreeSet::new();
2216        self.extend_btree_set(&mut out);
2217        out
2218    }
2219}
2220
2221fn token_bit_slot(symbol: i32) -> Option<usize> {
2222    if symbol == TOKEN_EOF {
2223        Some(0)
2224    } else if symbol > 0 {
2225        usize::try_from(symbol).ok()
2226    } else {
2227        None
2228    }
2229}
2230
2231fn token_bit_symbol(slot: usize) -> Option<i32> {
2232    if slot == 0 {
2233        Some(TOKEN_EOF)
2234    } else {
2235        i32::try_from(slot).ok()
2236    }
2237}
2238
2239/// Converts one consuming transition into the token types that would satisfy it
2240/// for diagnostic reporting.
2241fn transition_expected_symbols(
2242    transition: ParserTransition<'_>,
2243    max_token_type: i32,
2244) -> BTreeSet<i32> {
2245    let mut symbols = BTreeSet::new();
2246    match &transition.data() {
2247        Transition::Atom { label, .. } => {
2248            symbols.insert(*label);
2249        }
2250        Transition::Range { start, stop, .. } => {
2251            symbols.extend(*start..=*stop);
2252        }
2253        Transition::Set { set, .. } => {
2254            for (start, stop) in set.ranges() {
2255                symbols.extend(start..=stop);
2256            }
2257        }
2258        Transition::NotSet { set, .. } => {
2259            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2260        }
2261        Transition::Wildcard { .. } => {
2262            symbols.extend(1..=max_token_type);
2263        }
2264        Transition::Epsilon { .. }
2265        | Transition::Rule { .. }
2266        | Transition::Predicate { .. }
2267        | Transition::Action { .. }
2268        | Transition::Precedence { .. } => {}
2269    }
2270    symbols
2271}
2272
2273fn transition_expected_token_set(
2274    transition: ParserTransition<'_>,
2275    max_token_type: i32,
2276) -> TokenBitSet {
2277    let mut symbols = TokenBitSet::default();
2278    match &transition.data() {
2279        Transition::Atom { label, .. } => {
2280            symbols.insert(*label);
2281        }
2282        Transition::Range { start, stop, .. } => {
2283            symbols.extend_range(*start, *stop);
2284        }
2285        Transition::Set { set, .. } => {
2286            for (start, stop) in set.ranges() {
2287                symbols.extend_range(start, stop);
2288            }
2289        }
2290        Transition::NotSet { set, .. } => {
2291            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2292        }
2293        Transition::Wildcard { .. } => {
2294            symbols.extend_range(1, max_token_type);
2295        }
2296        Transition::Epsilon { .. }
2297        | Transition::Rule { .. }
2298        | Transition::Predicate { .. }
2299        | Transition::Action { .. }
2300        | Transition::Precedence { .. } => {}
2301    }
2302    symbols
2303}
2304
2305/// Returns the consuming-token expectations reachable from an ATN state through
2306/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2307/// and loop exits report the same expectation set ANTLR users see.
2308fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2309    let mut symbols = BTreeSet::new();
2310    let mut stack = vec![state_number];
2311    let mut visited = BTreeSet::new();
2312    while let Some(current) = stack.pop() {
2313        if !visited.insert(current) {
2314            continue;
2315        }
2316        let Some(state) = atn.state(current) else {
2317            continue;
2318        };
2319        for transition in &state.transitions() {
2320            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2321            if transition_symbols.is_empty() {
2322                if transition.is_epsilon() {
2323                    stack.push(transition.target());
2324                }
2325            } else {
2326                symbols.extend(transition_symbols);
2327            }
2328        }
2329    }
2330    symbols
2331}
2332
2333fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2334    let mut symbols = TokenBitSet::default();
2335    let mut stack = vec![state_number];
2336    let mut visited = BTreeSet::new();
2337    while let Some(current) = stack.pop() {
2338        if !visited.insert(current) {
2339            continue;
2340        }
2341        let Some(state) = atn.state(current) else {
2342            continue;
2343        };
2344        for transition in &state.transitions() {
2345            let transition_symbols =
2346                transition_expected_token_set(transition, atn.max_token_type());
2347            if transition_symbols.is_empty() {
2348                if transition.is_epsilon() {
2349                    stack.push(transition.target());
2350                }
2351            } else {
2352                symbols.extend_from(&transition_symbols);
2353            }
2354        }
2355    }
2356    symbols
2357}
2358
2359fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2360    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2361        return false;
2362    };
2363    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2364        return false;
2365    };
2366    epsilon_reaches_state(atn, state_number, stop_state)
2367}
2368
2369fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2370    let mut stack = vec![start];
2371    let mut visited = BTreeSet::new();
2372    while let Some(current) = stack.pop() {
2373        if current == target {
2374            return true;
2375        }
2376        if !visited.insert(current) {
2377            continue;
2378        }
2379        let Some(state) = atn.state(current) else {
2380            continue;
2381        };
2382        stack.extend(
2383            state
2384                .transitions()
2385                .iter()
2386                .filter(|transition| transition.is_epsilon())
2387                .map(ParserTransition::target),
2388        );
2389    }
2390    false
2391}
2392
2393/// FIRST set for a rule entry plus whether the rule is nullable.
2394///
2395/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2396/// a consuming transition or reaches the rule's stop state. Used by the fast
2397/// recognizer to skip rule alternatives whose first-consumed token cannot
2398/// possibly match the current lookahead.
2399#[derive(Clone, Debug, Default, Eq, PartialEq)]
2400struct FirstSet {
2401    symbols: TokenBitSet,
2402    nullable: bool,
2403}
2404
2405/// Per-parser cache of FIRST sets computed during recognition. The fast path
2406/// consults this on every speculative `Transition::Rule` encounter, so the
2407/// computation must amortize across all of those calls — the FIRST set is a
2408/// pure function of the ATN, not of the input position. Cached entries are
2409/// shared via `Rc` so the recognizer never deep-copies the underlying
2410/// `BTreeSet<i32>`.
2411type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2412
2413// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2414// and decision-lookahead entries are purely functions of the grammar's
2415// ATN, so caching across parses lets repeated parsing of the same grammar
2416// (the common case for a CLI tool or language server) avoid redoing the
2417// closure work. Generated parsers hand us a `&'static Atn` whose address
2418// is stable, which is what we hash on.
2419type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2420
2421#[derive(Debug, Default)]
2422struct LeftRecursiveOperatorLookahead {
2423    /// Operator alts whose token-prefix is fully matched by this one symbol
2424    /// (then only epsilons/actions remain before the recursive RHS call).
2425    /// Safe for one-token loop-enter fast path.
2426    single_token: TokenBitSet,
2427    /// Operator alts that start with this symbol but still require more tokens
2428    /// before the operand. Must not force enter from one-token lookahead when a
2429    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2430    /// has to weigh the exit alt as well.
2431    multi_token_prefix: TokenBitSet,
2432    predicate_dependent: TokenBitSet,
2433}
2434
2435#[derive(Default)]
2436struct SharedAtnCache {
2437    first_set: FirstSetCache,
2438    decision_lookahead: DecisionLookaheadCache,
2439    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2440    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2441    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2442    rule_stop_reach: FxHashMap<usize, bool>,
2443    observable_action_transitions: Option<bool>,
2444    predicate_transitions: Option<bool>,
2445}
2446
2447thread_local! {
2448    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2449        RefCell::new(FxHashMap::default());
2450}
2451
2452/// Compound key for `SHARED_ATN_CACHES`.
2453///
2454/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2455/// pointer identifies one grammar for the program's lifetime. For the
2456/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2457/// the secondary fields below catch the pointer collision: a new grammar
2458/// would need to match all of `(states ptr, states len, max_token_type)` to
2459/// be mistaken for the dropped one. That combination changing under us
2460/// without a rebuild is implausible enough to treat as a bug; bundling them
2461/// into the key is otherwise a few extra bytes per lookup.
2462#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2463struct SharedAtnCacheKey {
2464    atn: usize,
2465    states: usize,
2466    state_count: usize,
2467    max_token_type: i32,
2468}
2469
2470impl SharedAtnCacheKey {
2471    fn for_atn(atn: &Atn) -> Self {
2472        let (states, state_count) = atn.storage_identity();
2473        Self {
2474            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2475            states,
2476            state_count,
2477            max_token_type: atn.max_token_type(),
2478        }
2479    }
2480}
2481
2482fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2483    SHARED_ATN_CACHES.with(|cell| {
2484        let key = SharedAtnCacheKey::for_atn(atn);
2485        let mut map = cell.borrow_mut();
2486        let cache = map.entry(key).or_default();
2487        f(&mut cache.first_set)
2488    })
2489}
2490
2491fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2492    SHARED_ATN_CACHES.with(|cell| {
2493        let key = SharedAtnCacheKey::for_atn(atn);
2494        let mut map = cell.borrow_mut();
2495        let cache = map.entry(key).or_default();
2496        f(cache)
2497    })
2498}
2499
2500/// Per-decision-state cached look-1 sets for each outgoing transition.
2501///
2502/// At a multi-alternative state, the recognizer would otherwise speculatively
2503/// walk every alternative even when only one can possibly accept the current
2504/// lookahead. Caching the look-1 set per transition lets us prune the
2505/// non-viable transitions before recursing — the same SLL prediction trick
2506/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2507/// filter rather than a full DFA.
2508#[derive(Debug, Default)]
2509struct DecisionLookahead {
2510    transitions: Vec<TransitionLookSet>,
2511}
2512
2513/// Look-1 information for one outgoing transition.
2514///
2515/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2516/// can reach the rule stop without consuming a token (e.g. an empty alt).
2517/// Nullable transitions cannot be pruned: they may still be the right path
2518/// when the lookahead consumes nothing further inside the current rule.
2519#[derive(Clone, Debug, Default)]
2520struct TransitionLookSet {
2521    symbols: TokenBitSet,
2522    nullable: bool,
2523}
2524
2525/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2526/// rarely-touched fields keeps the recursive helpers below the function-arity
2527/// lint and lets every nested call thread the same cache and cycle guards.
2528struct FirstSetCtx<'a> {
2529    cache: &'a mut FirstSetCache,
2530    in_progress: BTreeSet<(usize, usize)>,
2531    hit_cycle: bool,
2532}
2533
2534/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2535/// shared cache and tolerating recursive nullable rule chains. Mutually
2536/// recursive rules cannot stack-overflow because callers in flight are tracked
2537/// in `ctx.in_progress`; revisits return without recursing, and the partial
2538/// result is cached only when no cycle was detected during its computation.
2539///
2540/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2541/// rule-call probes only pay a reference bump.
2542fn rule_first_set(
2543    atn: &Atn,
2544    target: usize,
2545    rule_stop_state: usize,
2546    cache: &mut FirstSetCache,
2547) -> Rc<FirstSet> {
2548    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2549        return Rc::clone(cached);
2550    }
2551    let mut ctx = FirstSetCtx {
2552        cache,
2553        in_progress: BTreeSet::new(),
2554        hit_cycle: false,
2555    };
2556    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2557}
2558
2559fn rule_first_set_cached(
2560    atn: &Atn,
2561    target: usize,
2562    rule_stop_state: usize,
2563    ctx: &mut FirstSetCtx<'_>,
2564) -> Rc<FirstSet> {
2565    let key = (target, rule_stop_state);
2566    if let Some(cached) = ctx.cache.get(&key) {
2567        return Rc::clone(cached);
2568    }
2569    if !ctx.in_progress.insert(key) {
2570        // Cycle: a caller above is already computing this entry. Return an
2571        // empty FIRST set; that caller's traversal supplies the contributions
2572        // from the rule's other alternatives.
2573        return Rc::new(FirstSet::default());
2574    }
2575    let saved_hit_cycle = ctx.hit_cycle;
2576    ctx.hit_cycle = false;
2577    let mut first = FirstSet::default();
2578    let mut visited = BTreeSet::new();
2579    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2580    ctx.in_progress.remove(&key);
2581    let entry = Rc::new(first);
2582    if !ctx.hit_cycle {
2583        ctx.cache.insert(key, Rc::clone(&entry));
2584    }
2585    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2586    entry
2587}
2588
2589/// Returns the look-1 set for traversing `transition` while still inside the
2590/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2591/// prunes transitions whose look-1 cannot accept the current lookahead.
2592fn transition_first_set(
2593    atn: &Atn,
2594    transition: ParserTransition<'_>,
2595    rule_stop_state: usize,
2596    cache: &mut FirstSetCache,
2597) -> TransitionLookSet {
2598    match &transition.data() {
2599        Transition::Atom { label, .. } => {
2600            let mut symbols = TokenBitSet::default();
2601            symbols.insert(*label);
2602            TransitionLookSet {
2603                symbols,
2604                nullable: false,
2605            }
2606        }
2607        Transition::Range { start, stop, .. } => {
2608            let mut symbols = TokenBitSet::default();
2609            symbols.extend_range(*start, *stop);
2610            TransitionLookSet {
2611                symbols,
2612                nullable: false,
2613            }
2614        }
2615        Transition::Set { set, .. } => {
2616            let mut symbols = TokenBitSet::default();
2617            for (start, stop) in set.ranges() {
2618                symbols.extend_range(start, stop);
2619            }
2620            TransitionLookSet {
2621                symbols,
2622                nullable: false,
2623            }
2624        }
2625        Transition::NotSet { set, .. } => {
2626            let max = atn.max_token_type();
2627            let mut symbols = TokenBitSet::default();
2628            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2629            TransitionLookSet {
2630                symbols,
2631                nullable: false,
2632            }
2633        }
2634        Transition::Wildcard { .. } => {
2635            let mut symbols = TokenBitSet::default();
2636            symbols.extend_range(1, atn.max_token_type());
2637            TransitionLookSet {
2638                symbols,
2639                nullable: false,
2640            }
2641        }
2642        Transition::Epsilon { target }
2643        | Transition::Action { target, .. }
2644        | Transition::Predicate { target, .. }
2645        | Transition::Precedence { target, .. } => {
2646            // Walk the closure starting at `target` until a consuming transition
2647            // is reached or the rule stop state is hit.
2648            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2649            TransitionLookSet {
2650                symbols: first.symbols.clone(),
2651                nullable: first.nullable,
2652            }
2653        }
2654        Transition::Rule {
2655            target,
2656            rule_index,
2657            follow_state,
2658            ..
2659        } => {
2660            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2661                return TransitionLookSet::default();
2662            };
2663            let child = rule_first_set(atn, *target, child_stop, cache);
2664            let mut symbols = child.symbols.clone();
2665            let nullable = if child.nullable {
2666                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2667                symbols.extend_from(&follow.symbols);
2668                follow.nullable
2669            } else {
2670                false
2671            };
2672            TransitionLookSet { symbols, nullable }
2673        }
2674    }
2675}
2676
2677/// Reports whether `transition` can be pruned at a multi-alt state because
2678/// its cached look-1 cannot accept the current lookahead.
2679///
2680/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2681/// Rule/Precedence) so consuming transitions still reach the
2682/// `matches`+recovery path that surfaces single-token deletion / insertion
2683/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2684/// transition is pruned, its FIRST set is folded into `expected` so failed
2685/// parses produce the same `mismatched input ... expecting ...` diagnostic
2686/// the no-prefilter baseline would emit.
2687/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2688/// one transition's FIRST set and no transition is nullable. Used as an
2689/// LL(1) commit point: when prediction is unambiguous from the lookahead
2690/// alone, the recursive recognizer can skip every other alt without paying
2691/// for the per-transition filter probe.
2692///
2693/// `None` signals the caller to fall back to per-transition lookahead
2694/// filtering. Returning `Some` for an alt whose transition cannot actually
2695/// match would prune the only viable parse path; this is why we require
2696/// strict disjointness *and* no nullable transitions in the decision.
2697fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2698    let mut chosen: Option<usize> = None;
2699    for (index, transition) in entry.transitions.iter().enumerate() {
2700        if transition.nullable {
2701            return None;
2702        }
2703        if transition.symbols.contains(symbol) {
2704            if chosen.is_some() {
2705                return None;
2706            }
2707            chosen = Some(index);
2708        }
2709    }
2710    chosen
2711}
2712
2713/// Returns the unique greedy alt index (0-based) selected by the current
2714/// lookahead.
2715///
2716/// The shortcut is intentionally conservative around nullable exits. If the
2717/// current symbol can start a consuming alternative and an empty alternative is
2718/// also present, one-token lookahead is not enough to know whether the symbol
2719/// belongs to the current construct or to its caller's follow set. `None`
2720/// signals the caller to fall back to adaptive prediction.
2721fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2722    let mut matching_non_nullable_alt = None;
2723    let mut nullable_alt = None;
2724    for (index, transition) in entry.transitions.iter().enumerate() {
2725        if transition.nullable {
2726            if nullable_alt.is_some() {
2727                return None;
2728            }
2729            nullable_alt = Some(index);
2730        }
2731        if transition.symbols.contains(symbol) {
2732            if transition.nullable {
2733                continue;
2734            }
2735            if matching_non_nullable_alt.is_some() {
2736                return None;
2737            }
2738            matching_non_nullable_alt = Some(index);
2739        }
2740    }
2741    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
2742        return None;
2743    }
2744    if non_greedy {
2745        nullable_alt.or(matching_non_nullable_alt)
2746    } else {
2747        matching_non_nullable_alt.or(nullable_alt)
2748    }
2749}
2750
2751fn should_skip_via_lookahead(
2752    transition_kind: ParserTransitionKind,
2753    transition_index: usize,
2754    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
2755    index: usize,
2756    record_expected: bool,
2757    expected: &mut ExpectedTokens,
2758) -> bool {
2759    let prune_non_consuming = matches!(
2760        transition_kind,
2761        ParserTransitionKind::Epsilon
2762            | ParserTransitionKind::Action
2763            | ParserTransitionKind::Predicate
2764            | ParserTransitionKind::Rule
2765            | ParserTransitionKind::Precedence
2766    );
2767    if !prune_non_consuming {
2768        return false;
2769    }
2770    let Some((symbol, entry)) = lookahead_filter else {
2771        return false;
2772    };
2773    let Some(set) = entry.transitions.get(transition_index) else {
2774        return false;
2775    };
2776    if set.symbols.contains(*symbol) || set.nullable {
2777        return false;
2778    }
2779    if record_expected && !set.symbols.is_empty() {
2780        record_pruned_transition_expected(set, index, expected);
2781    }
2782    true
2783}
2784
2785fn should_skip_rule_via_first_set(
2786    first: &FirstSet,
2787    symbol: i32,
2788    record_expected: bool,
2789    index: usize,
2790    expected: &mut ExpectedTokens,
2791) -> bool {
2792    if first.nullable || first.symbols.contains(symbol) {
2793        return false;
2794    }
2795    if record_expected && !first.symbols.is_empty() {
2796        record_token_bit_expected(&first.symbols, index, expected);
2797    }
2798    true
2799}
2800
2801fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
2802    match expected.index {
2803        Some(current) if index < current => {}
2804        Some(current) if index == current => {
2805            symbols.extend_btree_set(&mut expected.symbols);
2806        }
2807        _ => {
2808            expected.index = Some(index);
2809            expected.symbols = symbols.to_btree_set();
2810        }
2811    }
2812}
2813
2814/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
2815fn record_pruned_transition_expected(
2816    set: &TransitionLookSet,
2817    index: usize,
2818    expected: &mut ExpectedTokens,
2819) {
2820    match expected.index {
2821        Some(current) if index < current => {}
2822        Some(current) if index == current => {
2823            set.symbols.extend_btree_set(&mut expected.symbols);
2824        }
2825        _ => {
2826            expected.index = Some(index);
2827            expected.symbols = set.symbols.to_btree_set();
2828        }
2829    }
2830}
2831
2832fn rule_first_set_inner(
2833    atn: &Atn,
2834    state_number: usize,
2835    rule_stop_state: usize,
2836    ctx: &mut FirstSetCtx<'_>,
2837    visited: &mut BTreeSet<usize>,
2838    first: &mut FirstSet,
2839) {
2840    if !visited.insert(state_number) {
2841        return;
2842    }
2843    if state_number == rule_stop_state {
2844        first.nullable = true;
2845        return;
2846    }
2847    let Some(state) = atn.state(state_number) else {
2848        return;
2849    };
2850    for transition in &state.transitions() {
2851        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2852        if !transition_symbols.is_empty() {
2853            first.symbols.extend_iter(transition_symbols);
2854            continue;
2855        }
2856        match &transition.data() {
2857            Transition::Epsilon { target }
2858            | Transition::Action { target, .. }
2859            | Transition::Predicate { target, .. }
2860            | Transition::Precedence { target, .. } => {
2861                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
2862            }
2863            Transition::Rule {
2864                target,
2865                rule_index,
2866                follow_state,
2867                ..
2868            } => {
2869                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2870                    continue;
2871                };
2872                let child_key = (*target, child_stop);
2873                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
2874                    ctx.hit_cycle = true;
2875                }
2876                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
2877                first.symbols.extend_from(&child.symbols);
2878                if child.nullable {
2879                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
2880                }
2881            }
2882            Transition::Atom { .. }
2883            | Transition::Range { .. }
2884            | Transition::Set { .. }
2885            | Transition::NotSet { .. }
2886            | Transition::Wildcard { .. } => {}
2887        }
2888    }
2889}
2890
2891/// Returns token types that can resume parsing from `state_number` after a
2892/// failed child rule, following rule calls as well as epsilon transitions.
2893fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
2894    let mut symbols = BTreeSet::new();
2895    state_sync_symbols_inner(
2896        atn,
2897        state_number,
2898        stop_state,
2899        &mut BTreeSet::new(),
2900        &mut symbols,
2901    );
2902    symbols
2903}
2904
2905/// Walks epsilon-like continuations from a parent follow state until it finds
2906/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
2907fn state_sync_symbols_inner(
2908    atn: &Atn,
2909    state_number: usize,
2910    stop_state: usize,
2911    visited: &mut BTreeSet<usize>,
2912    symbols: &mut BTreeSet<i32>,
2913) {
2914    if !visited.insert(state_number) {
2915        return;
2916    }
2917    if state_number == stop_state {
2918        symbols.insert(TOKEN_EOF);
2919        return;
2920    }
2921    let Some(state) = atn.state(state_number) else {
2922        return;
2923    };
2924    for transition in &state.transitions() {
2925        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2926        if transition_symbols.is_empty() {
2927            match &transition.data() {
2928                Transition::Rule { target, .. }
2929                | Transition::Epsilon { target }
2930                | Transition::Action { target, .. }
2931                | Transition::Predicate { target, .. }
2932                | Transition::Precedence { target, .. } => {
2933                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
2934                }
2935                Transition::Atom { .. }
2936                | Transition::Range { .. }
2937                | Transition::Set { .. }
2938                | Transition::NotSet { .. }
2939                | Transition::Wildcard { .. } => {}
2940            }
2941        } else {
2942            symbols.extend(transition_symbols);
2943        }
2944    }
2945}
2946
2947#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2948struct OperatorSymbolReachability {
2949    /// One token completes an unconditional operator token-prefix.
2950    single_token: bool,
2951    /// An unconditional operator path requires more tokens before its operand.
2952    multi_token: bool,
2953    /// At least one matching operator path depends on a semantic predicate.
2954    predicate_dependent: bool,
2955}
2956
2957impl OperatorSymbolReachability {
2958    const ADAPTIVE_FALLBACK: Self = Self {
2959        single_token: false,
2960        multi_token: false,
2961        predicate_dependent: true,
2962    };
2963
2964    const fn single_token(predicate_dependent: bool) -> Self {
2965        if predicate_dependent {
2966            Self {
2967                single_token: false,
2968                multi_token: false,
2969                predicate_dependent: true,
2970            }
2971        } else {
2972            Self {
2973                single_token: true,
2974                multi_token: false,
2975                predicate_dependent: false,
2976            }
2977        }
2978    }
2979
2980    const fn multi_token(predicate_dependent: bool) -> Self {
2981        if predicate_dependent {
2982            Self {
2983                single_token: false,
2984                multi_token: false,
2985                predicate_dependent: true,
2986            }
2987        } else {
2988            Self {
2989                single_token: false,
2990                multi_token: true,
2991                predicate_dependent: false,
2992            }
2993        }
2994    }
2995
2996    const fn union(self, other: Self) -> Self {
2997        Self {
2998            single_token: self.single_token || other.single_token,
2999            multi_token: self.multi_token || other.multi_token,
3000            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3001        }
3002    }
3003}
3004
3005#[derive(Clone, Copy)]
3006struct OperatorReachabilityRequest {
3007    symbol: i32,
3008    precedence: i32,
3009    predicate_dependent: bool,
3010    operator_rule_index: usize,
3011}
3012
3013#[derive(Clone, Copy, Debug)]
3014struct OperatorRuleContinuation {
3015    stop_state: usize,
3016    follow_state: usize,
3017    return_precedence: i32,
3018}
3019
3020struct NullablePrecedenceCtx {
3021    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3022    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3023    hit_cycle: bool,
3024}
3025
3026fn state_is_nullable_with_precedence(
3027    atn: &Atn,
3028    state_number: usize,
3029    stop_state_number: usize,
3030    precedence: i32,
3031    allow_predicates: bool,
3032    ctx: &mut NullablePrecedenceCtx,
3033) -> bool {
3034    let saved_hit_cycle = ctx.hit_cycle;
3035    ctx.hit_cycle = false;
3036    let nullable = state_is_nullable_with_precedence_cached(
3037        atn,
3038        state_number,
3039        stop_state_number,
3040        precedence,
3041        allow_predicates,
3042        ctx,
3043    );
3044    ctx.hit_cycle = saved_hit_cycle;
3045    nullable
3046}
3047
3048fn state_is_nullable_with_precedence_cached(
3049    atn: &Atn,
3050    state_number: usize,
3051    stop_state_number: usize,
3052    precedence: i32,
3053    allow_predicates: bool,
3054    ctx: &mut NullablePrecedenceCtx,
3055) -> bool {
3056    if state_number == stop_state_number {
3057        return true;
3058    }
3059    let key = (
3060        state_number,
3061        stop_state_number,
3062        precedence,
3063        allow_predicates,
3064    );
3065    if let Some(cached) = ctx.cache.get(&key) {
3066        return *cached;
3067    }
3068    if !ctx.in_progress.insert(key) {
3069        ctx.hit_cycle = true;
3070        return false;
3071    }
3072    let saved_hit_cycle = ctx.hit_cycle;
3073    ctx.hit_cycle = false;
3074    let nullable = atn.state(state_number).is_some_and(|state| {
3075        state
3076            .transitions()
3077            .iter()
3078            .any(|transition| match &transition.data() {
3079                Transition::Rule {
3080                    target,
3081                    rule_index,
3082                    follow_state,
3083                    precedence: rule_precedence,
3084                } => {
3085                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3086                        return false;
3087                    };
3088                    state_is_nullable_with_precedence_cached(
3089                        atn,
3090                        *target,
3091                        child_stop,
3092                        *rule_precedence,
3093                        allow_predicates,
3094                        ctx,
3095                    ) && state_is_nullable_with_precedence_cached(
3096                        atn,
3097                        *follow_state,
3098                        stop_state_number,
3099                        precedence,
3100                        allow_predicates,
3101                        ctx,
3102                    )
3103                }
3104                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3105                    state_is_nullable_with_precedence_cached(
3106                        atn,
3107                        *target,
3108                        stop_state_number,
3109                        precedence,
3110                        allow_predicates,
3111                        ctx,
3112                    )
3113                }
3114                Transition::Predicate { target, .. } if allow_predicates => {
3115                    state_is_nullable_with_precedence_cached(
3116                        atn,
3117                        *target,
3118                        stop_state_number,
3119                        precedence,
3120                        allow_predicates,
3121                        ctx,
3122                    )
3123                }
3124                Transition::Precedence {
3125                    target,
3126                    precedence: transition_precedence,
3127                } if *transition_precedence >= precedence => {
3128                    state_is_nullable_with_precedence_cached(
3129                        atn,
3130                        *target,
3131                        stop_state_number,
3132                        precedence,
3133                        allow_predicates,
3134                        ctx,
3135                    )
3136                }
3137                Transition::Atom { .. }
3138                | Transition::Range { .. }
3139                | Transition::Set { .. }
3140                | Transition::NotSet { .. }
3141                | Transition::Wildcard { .. }
3142                | Transition::Predicate { .. }
3143                | Transition::Precedence { .. } => false,
3144            })
3145    });
3146    ctx.in_progress.remove(&key);
3147    if !ctx.hit_cycle {
3148        ctx.cache.insert(key, nullable);
3149    }
3150    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3151    nullable
3152}
3153
3154/// Classifies what remains after the operator's first token is matched.
3155fn state_operator_token_prefix_reachability(
3156    atn: &Atn,
3157    state_number: usize,
3158    request: OperatorReachabilityRequest,
3159    continuations: &[OperatorRuleContinuation],
3160    visited: &mut BTreeSet<(usize, i32, bool)>,
3161) -> OperatorSymbolReachability {
3162    let key = (
3163        state_number,
3164        request.precedence,
3165        request.predicate_dependent,
3166    );
3167    if !visited.insert(key) {
3168        // Recursive helper rules can grow the return stack without consuming
3169        // input. Delegate cycles to adaptive prediction instead of forcing a
3170        // potentially incomplete one-token answer.
3171        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3172    }
3173    if let Some((continuation, remaining)) = continuations.split_last()
3174        && state_number == continuation.stop_state
3175    {
3176        let result = state_operator_token_prefix_reachability(
3177            atn,
3178            continuation.follow_state,
3179            OperatorReachabilityRequest {
3180                precedence: continuation.return_precedence,
3181                ..request
3182            },
3183            remaining,
3184            visited,
3185        );
3186        visited.remove(&key);
3187        return result;
3188    }
3189    let Some(state) = atn.state(state_number) else {
3190        visited.remove(&key);
3191        return OperatorSymbolReachability::default();
3192    };
3193    let completes_operator = match state.kind() {
3194        AtnStateKind::RuleStop => continuations.is_empty(),
3195        AtnStateKind::StarLoopBack
3196        | AtnStateKind::StarLoopEntry
3197        | AtnStateKind::PlusLoopBack
3198        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3199        _ => false,
3200    };
3201    if completes_operator {
3202        visited.remove(&key);
3203        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3204    }
3205    let mut reachability = OperatorSymbolReachability::default();
3206    for transition in &state.transitions() {
3207        let transition_reachability = match &transition.data() {
3208            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3209                OperatorSymbolReachability::single_token(request.predicate_dependent)
3210            }
3211            Transition::Rule {
3212                target,
3213                rule_index,
3214                follow_state,
3215                precedence: rule_precedence,
3216            } => {
3217                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3218                    continue;
3219                };
3220                let mut nested = continuations.to_vec();
3221                nested.push(OperatorRuleContinuation {
3222                    stop_state: child_stop,
3223                    follow_state: *follow_state,
3224                    return_precedence: request.precedence,
3225                });
3226                state_operator_token_prefix_reachability(
3227                    atn,
3228                    *target,
3229                    OperatorReachabilityRequest {
3230                        precedence: *rule_precedence,
3231                        ..request
3232                    },
3233                    &nested,
3234                    visited,
3235                )
3236            }
3237            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3238                state_operator_token_prefix_reachability(
3239                    atn,
3240                    *target,
3241                    request,
3242                    continuations,
3243                    visited,
3244                )
3245            }
3246            Transition::Precedence {
3247                target,
3248                precedence: transition_precedence,
3249            } => {
3250                if *transition_precedence < request.precedence {
3251                    OperatorSymbolReachability::default()
3252                } else {
3253                    state_operator_token_prefix_reachability(
3254                        atn,
3255                        *target,
3256                        request,
3257                        continuations,
3258                        visited,
3259                    )
3260                }
3261            }
3262            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3263                atn,
3264                *target,
3265                OperatorReachabilityRequest {
3266                    predicate_dependent: true,
3267                    ..request
3268                },
3269                continuations,
3270                visited,
3271            ),
3272            Transition::Atom { .. }
3273            | Transition::Range { .. }
3274            | Transition::Set { .. }
3275            | Transition::NotSet { .. }
3276            | Transition::Wildcard { .. } => {
3277                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3278            }
3279        };
3280        reachability = reachability.union(transition_reachability);
3281    }
3282    visited.remove(&key);
3283    reachability
3284}
3285
3286fn state_can_reach_symbol_with_precedence(
3287    atn: &Atn,
3288    state_number: usize,
3289    request: OperatorReachabilityRequest,
3290    nullable_ctx: &mut NullablePrecedenceCtx,
3291    continuations: &mut Vec<OperatorRuleContinuation>,
3292    visited: &mut BTreeSet<(usize, i32, bool)>,
3293) -> OperatorSymbolReachability {
3294    let key = (
3295        state_number,
3296        request.precedence,
3297        request.predicate_dependent,
3298    );
3299    if !visited.insert(key) {
3300        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3301    }
3302    let Some(state) = atn.state(state_number) else {
3303        visited.remove(&key);
3304        return OperatorSymbolReachability::default();
3305    };
3306    let mut reachability = OperatorSymbolReachability::default();
3307    for transition in &state.transitions() {
3308        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3309            reachability = reachability.union(state_operator_token_prefix_reachability(
3310                atn,
3311                transition.target(),
3312                request,
3313                continuations,
3314                &mut BTreeSet::new(),
3315            ));
3316            continue;
3317        }
3318        let transition_reachability = match &transition.data() {
3319            Transition::Rule {
3320                target,
3321                rule_index,
3322                follow_state,
3323                precedence: rule_precedence,
3324            } => {
3325                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3326                    continue;
3327                };
3328                continuations.push(OperatorRuleContinuation {
3329                    stop_state: child_stop,
3330                    follow_state: *follow_state,
3331                    return_precedence: request.precedence,
3332                });
3333                let mut result = state_can_reach_symbol_with_precedence(
3334                    atn,
3335                    *target,
3336                    OperatorReachabilityRequest {
3337                        precedence: *rule_precedence,
3338                        ..request
3339                    },
3340                    nullable_ctx,
3341                    continuations,
3342                    visited,
3343                );
3344                continuations.pop();
3345                if state_is_nullable_with_precedence(
3346                    atn,
3347                    *target,
3348                    child_stop,
3349                    *rule_precedence,
3350                    true,
3351                    nullable_ctx,
3352                ) {
3353                    let child_predicate_dependent = request.predicate_dependent
3354                        || !state_is_nullable_with_precedence(
3355                            atn,
3356                            *target,
3357                            child_stop,
3358                            *rule_precedence,
3359                            false,
3360                            nullable_ctx,
3361                        );
3362                    result = result.union(state_can_reach_symbol_with_precedence(
3363                        atn,
3364                        *follow_state,
3365                        OperatorReachabilityRequest {
3366                            predicate_dependent: child_predicate_dependent,
3367                            ..request
3368                        },
3369                        nullable_ctx,
3370                        continuations,
3371                        visited,
3372                    ));
3373                }
3374                result
3375            }
3376            Transition::Epsilon { target }
3377            | Transition::Action { target, .. }
3378            | Transition::Precedence { target, .. } => {
3379                if matches!(
3380                    &transition.data(),
3381                    Transition::Precedence {
3382                        precedence: transition_precedence,
3383                        ..
3384                    } if *transition_precedence < request.precedence
3385                ) {
3386                    continue;
3387                }
3388                state_can_reach_symbol_with_precedence(
3389                    atn,
3390                    *target,
3391                    request,
3392                    nullable_ctx,
3393                    continuations,
3394                    visited,
3395                )
3396            }
3397            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3398                atn,
3399                *target,
3400                OperatorReachabilityRequest {
3401                    predicate_dependent: true,
3402                    ..request
3403                },
3404                nullable_ctx,
3405                continuations,
3406                visited,
3407            ),
3408            Transition::Atom { .. }
3409            | Transition::Range { .. }
3410            | Transition::Set { .. }
3411            | Transition::NotSet { .. }
3412            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3413        };
3414        reachability = reachability.union(transition_reachability);
3415    }
3416    visited.remove(&key);
3417    reachability
3418}
3419
3420fn left_recursive_operator_lookahead(
3421    atn: &Atn,
3422    state_number: usize,
3423    precedence: i32,
3424) -> LeftRecursiveOperatorLookahead {
3425    let Some(state) = atn.state(state_number) else {
3426        return LeftRecursiveOperatorLookahead::default();
3427    };
3428    let Some(operator_rule_index) = state.rule_index() else {
3429        return LeftRecursiveOperatorLookahead::default();
3430    };
3431    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3432    let mut nullable_ctx = NullablePrecedenceCtx {
3433        cache: FxHashMap::default(),
3434        in_progress: BTreeSet::new(),
3435        hit_cycle: false,
3436    };
3437    for transition in &state.transitions() {
3438        let target = transition.target();
3439        if atn
3440            .state(target)
3441            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3442        {
3443            continue;
3444        }
3445        for symbol in 1..=atn.max_token_type() {
3446            let reachability = state_can_reach_symbol_with_precedence(
3447                atn,
3448                target,
3449                OperatorReachabilityRequest {
3450                    symbol,
3451                    precedence,
3452                    predicate_dependent: false,
3453                    operator_rule_index,
3454                },
3455                &mut nullable_ctx,
3456                &mut Vec::new(),
3457                &mut BTreeSet::new(),
3458            );
3459            if reachability.single_token {
3460                lookahead.single_token.insert(symbol);
3461            }
3462            if reachability.multi_token {
3463                lookahead.multi_token_prefix.insert(symbol);
3464            }
3465            if reachability.predicate_dependent {
3466                lookahead.predicate_dependent.insert(symbol);
3467            }
3468        }
3469    }
3470    lookahead
3471}
3472
3473#[derive(Debug, Default)]
3474struct StateBeforeStopLookahead {
3475    symbols: TokenBitSet,
3476    reaches_context_boundary: bool,
3477}
3478
3479fn state_before_stop_lookahead(
3480    atn: &Atn,
3481    state_number: usize,
3482    stop_state_number: usize,
3483) -> Rc<StateBeforeStopLookahead> {
3484    with_shared_atn_caches(atn, |cache| {
3485        let key = (state_number, stop_state_number);
3486        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3487            return Rc::clone(cached);
3488        }
3489        let mut lookahead = StateBeforeStopLookahead::default();
3490        state_before_stop_lookahead_inner(
3491            atn,
3492            state_number,
3493            stop_state_number,
3494            &mut BTreeSet::new(),
3495            &mut cache.first_set,
3496            &mut lookahead,
3497        );
3498        let lookahead = Rc::new(lookahead);
3499        cache
3500            .state_before_stop_lookahead
3501            .insert(key, Rc::clone(&lookahead));
3502        lookahead
3503    })
3504}
3505
3506fn state_before_stop_lookahead_inner(
3507    atn: &Atn,
3508    state_number: usize,
3509    stop_state_number: usize,
3510    visited: &mut BTreeSet<usize>,
3511    first_set_cache: &mut FirstSetCache,
3512    lookahead: &mut StateBeforeStopLookahead,
3513) {
3514    if state_number == stop_state_number {
3515        lookahead.reaches_context_boundary = true;
3516        return;
3517    }
3518    if !visited.insert(state_number) {
3519        return;
3520    }
3521    let Some(state) = atn.state(state_number) else {
3522        return;
3523    };
3524    if state.kind() == AtnStateKind::RuleStop {
3525        lookahead.reaches_context_boundary = true;
3526        return;
3527    }
3528    for transition in &state.transitions() {
3529        match &transition.data() {
3530            Transition::Epsilon { target }
3531            | Transition::Action { target, .. }
3532            | Transition::Predicate { target, .. }
3533            | Transition::Precedence { target, .. } => {
3534                state_before_stop_lookahead_inner(
3535                    atn,
3536                    *target,
3537                    stop_state_number,
3538                    visited,
3539                    first_set_cache,
3540                    lookahead,
3541                );
3542            }
3543            Transition::Rule {
3544                target,
3545                rule_index,
3546                follow_state,
3547                ..
3548            } => {
3549                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3550                    continue;
3551                };
3552                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3553                lookahead.symbols.extend_from(&child.symbols);
3554                if child.nullable {
3555                    state_before_stop_lookahead_inner(
3556                        atn,
3557                        *follow_state,
3558                        stop_state_number,
3559                        visited,
3560                        first_set_cache,
3561                        lookahead,
3562                    );
3563                }
3564            }
3565            Transition::Atom { .. }
3566            | Transition::Range { .. }
3567            | Transition::Set { .. }
3568            | Transition::NotSet { .. }
3569            | Transition::Wildcard { .. } => {
3570                lookahead.symbols.extend_iter(transition_expected_symbols(
3571                    transition,
3572                    atn.max_token_type(),
3573                ));
3574            }
3575        }
3576    }
3577}
3578
3579fn caller_context_can_match_symbol_before_state(
3580    atn: &Atn,
3581    return_states: impl DoubleEndedIterator<Item = usize>,
3582    stop_state_number: usize,
3583    symbol: i32,
3584) -> bool {
3585    for return_state in return_states.rev() {
3586        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3587        if lookahead.symbols.contains(symbol) {
3588            return true;
3589        }
3590        if !lookahead.reaches_context_boundary {
3591            return false;
3592        }
3593    }
3594    false
3595}
3596
3597/// Carries recovery expectations and their restart state through epsilon-only
3598/// paths. ANTLR can report and repair at the decision state even when the
3599/// failed consuming transition is nested under block or loop epsilon edges.
3600fn next_recovery_context(
3601    atn: &Atn,
3602    state: AtnState<'_>,
3603    inherited: &BTreeSet<i32>,
3604    inherited_state: Option<usize>,
3605) -> (BTreeSet<i32>, Option<usize>) {
3606    let state_symbols = state_expected_symbols(atn, state.state_number());
3607    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3608        let mut symbols = state_symbols;
3609        symbols.extend(inherited.iter().copied());
3610        return (symbols, Some(state.state_number()));
3611    }
3612    (inherited.clone(), inherited_state)
3613}
3614
3615fn recovery_expected_symbols(
3616    atn: &Atn,
3617    state_number: usize,
3618    inherited: &BTreeSet<i32>,
3619) -> BTreeSet<i32> {
3620    let mut symbols = state_expected_symbols(atn, state_number);
3621    symbols.extend(inherited.iter().copied());
3622    symbols
3623}
3624
3625/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3626/// parser's cached state-expected-symbols sets and the inherited `Rc`
3627/// without copying when the state cannot widen recovery.
3628fn fast_next_recovery_context<S, H>(
3629    parser: &mut BaseParser<S, H>,
3630    atn: &Atn,
3631    state: AtnState<'_>,
3632    inherited: &Rc<BTreeSet<i32>>,
3633    inherited_state: Option<usize>,
3634) -> (Rc<BTreeSet<i32>>, Option<usize>)
3635where
3636    S: TokenSource,
3637    H: SemanticHooks,
3638{
3639    if state.transitions().len() <= 1 {
3640        return (Rc::clone(inherited), inherited_state);
3641    }
3642    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3643    if state_symbols.is_empty() {
3644        return (Rc::clone(inherited), inherited_state);
3645    }
3646    if inherited.is_empty() {
3647        return (state_symbols, Some(state.state_number()));
3648    }
3649    if Rc::ptr_eq(&state_symbols, inherited) {
3650        return (state_symbols, Some(state.state_number()));
3651    }
3652    let mut combined = (*state_symbols).clone();
3653    combined.extend(inherited.iter().copied());
3654    (
3655        parser.intern_recovery_symbols(combined),
3656        Some(state.state_number()),
3657    )
3658}
3659
3660/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3661/// cached state-expected-symbols and avoids cloning when no widening is
3662/// needed.
3663fn fast_recovery_expected_symbols<S, H>(
3664    parser: &mut BaseParser<S, H>,
3665    atn: &Atn,
3666    state_number: usize,
3667    inherited: &Rc<BTreeSet<i32>>,
3668) -> Rc<BTreeSet<i32>>
3669where
3670    S: TokenSource,
3671    H: SemanticHooks,
3672{
3673    let cached = parser.cached_state_expected_symbols(atn, state_number);
3674    if inherited.is_empty() {
3675        return cached;
3676    }
3677    if cached.is_empty() {
3678        return Rc::clone(inherited);
3679    }
3680    if Rc::ptr_eq(&cached, inherited) {
3681        return cached;
3682    }
3683    let mut combined = (*cached).clone();
3684    combined.extend(inherited.iter().copied());
3685    parser.intern_recovery_symbols(combined)
3686}
3687
3688struct ParserTableSemCtx<'a> {
3689    member_values: &'a mut BTreeMap<usize, i64>,
3690    return_values: &'a mut BTreeMap<String, i64>,
3691}
3692
3693impl semir::PredContext for ParserTableSemCtx<'_> {
3694    type TokenText<'a>
3695        = &'a str
3696    where
3697        Self: 'a;
3698
3699    fn la(&mut self, _offset: isize) -> i64 {
3700        i64::from(TOKEN_EOF)
3701    }
3702
3703    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3704        None
3705    }
3706
3707    fn token_index_adjacent(&mut self) -> bool {
3708        false
3709    }
3710
3711    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3712        None
3713    }
3714
3715    fn member(&self, member: usize) -> Option<i64> {
3716        Some(self.member_values.get(&member).copied().unwrap_or_default())
3717    }
3718
3719    fn local_arg(&self) -> Option<i64> {
3720        None
3721    }
3722
3723    fn column(&self) -> Option<i64> {
3724        None
3725    }
3726
3727    fn token_start_column(&self) -> Option<i64> {
3728        None
3729    }
3730
3731    fn token_text_so_far(&self) -> Option<String> {
3732        None
3733    }
3734
3735    fn hook(&mut self, _hook: HookId) -> bool {
3736        false
3737    }
3738}
3739
3740impl semir::ActContext for ParserTableSemCtx<'_> {
3741    fn set_member(&mut self, member: usize, value: i64) {
3742        self.member_values.insert(member, value);
3743    }
3744
3745    fn set_return(&mut self, name: &str, value: i64) {
3746        self.return_values.insert(name.to_owned(), value);
3747    }
3748
3749    fn action_hook(&mut self, _hook: HookId) {}
3750}
3751
3752/// Applies generated integer-member side effects to one speculative path.
3753fn apply_member_actions(
3754    source_state: usize,
3755    actions: &[ParserMemberAction],
3756    semantics: Option<&ParserSemantics>,
3757    values: &mut BTreeMap<usize, i64>,
3758) {
3759    for action in actions
3760        .iter()
3761        .filter(|action| action.source_state == source_state)
3762    {
3763        *values.entry(action.member).or_default() += action.delta;
3764    }
3765    let Some(semantics) = semantics else {
3766        return;
3767    };
3768    let mut return_values = BTreeMap::new();
3769    let mut ctx = ParserTableSemCtx {
3770        member_values: values,
3771        return_values: &mut return_values,
3772    };
3773    for action in semantics
3774        .actions
3775        .iter()
3776        .filter(|action| action.source_state == source_state && action.speculative)
3777    {
3778        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3779    }
3780}
3781
3782/// Returns the speculative member state after replaying one ATN action state.
3783fn member_values_after_action(
3784    source_state: usize,
3785    actions: &[ParserMemberAction],
3786    semantics: Option<&ParserSemantics>,
3787    values: &BTreeMap<usize, i64>,
3788) -> BTreeMap<usize, i64> {
3789    let mut values = values.clone();
3790    apply_member_actions(source_state, actions, semantics, &mut values);
3791    values
3792}
3793
3794/// Returns the speculative rule-return state after replaying one ATN action.
3795fn return_values_after_action(
3796    source_state: usize,
3797    rule_index: usize,
3798    actions: &[ParserReturnAction],
3799    semantics: Option<&ParserSemantics>,
3800    values: &BTreeMap<String, i64>,
3801) -> BTreeMap<String, i64> {
3802    let mut values = values.clone();
3803    for action in actions
3804        .iter()
3805        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
3806    {
3807        values.insert(action.name.to_owned(), action.value);
3808    }
3809    if let Some(semantics) = semantics {
3810        let mut member_values = BTreeMap::new();
3811        let mut ctx = ParserTableSemCtx {
3812            member_values: &mut member_values,
3813            return_values: &mut values,
3814        };
3815        for action in semantics.actions.iter().filter(|action| {
3816            action.source_state == source_state
3817                && action.rule_index == rule_index
3818                && !action.speculative
3819        }) {
3820            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3821        }
3822    }
3823    values
3824}
3825
3826/// Resolves the integer argument visible to a child rule invocation.
3827fn rule_local_int_arg(
3828    rule_args: &[ParserRuleArg],
3829    source_state: usize,
3830    rule_index: usize,
3831    local_int_arg: Option<(usize, i64)>,
3832) -> Option<(usize, i64)> {
3833    rule_args
3834        .iter()
3835        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
3836        .map(|arg| {
3837            let value = if arg.inherit_local {
3838                local_int_arg.map_or(arg.value, |(_, value)| value)
3839            } else {
3840                arg.value
3841            };
3842            (rule_index, value)
3843        })
3844}
3845
3846/// Builds the terminal recognition outcome for a path that reached its stop
3847/// state.
3848fn stop_outcome(
3849    index: usize,
3850    consumed_eof: bool,
3851    rule_alt_number: usize,
3852    member_values: BTreeMap<usize, i64>,
3853    return_values: BTreeMap<String, i64>,
3854) -> Vec<RecognizeOutcome> {
3855    vec![RecognizeOutcome {
3856        index,
3857        consumed_eof,
3858        alt_number: rule_alt_number,
3859        member_values,
3860        return_values,
3861        diagnostics: DiagnosticSeqId::EMPTY,
3862        decisions: Vec::new(),
3863        actions: Vec::new(),
3864        nodes: NodeSeqId::EMPTY,
3865    }]
3866}
3867
3868fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
3869    with_shared_atn_caches(atn, |cache| {
3870        *cache.observable_action_transitions.get_or_insert_with(|| {
3871            atn.states().any(|state| {
3872                state.transitions().iter().any(|transition| {
3873                    matches!(
3874                        &transition.data(),
3875                        Transition::Action {
3876                            action_index: Some(_),
3877                            ..
3878                        }
3879                    )
3880                })
3881            })
3882        })
3883    })
3884}
3885
3886fn atn_has_predicate_transitions(atn: &Atn) -> bool {
3887    with_shared_atn_caches(atn, |cache| {
3888        *cache.predicate_transitions.get_or_insert_with(|| {
3889            atn.states().any(|state| {
3890                state
3891                    .transitions()
3892                    .iter()
3893                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
3894            })
3895        })
3896    })
3897}
3898
3899/// Reports whether predicates are the only observable semantics the fast
3900/// recognizer must preserve. Without path-local actions, arguments, or return
3901/// state, repeated evaluation at one coordinate and input index receives the
3902/// same runtime context.
3903fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
3904    options.init_action_rules.is_empty()
3905        && !options.track_alt_numbers
3906        && options
3907            .predicates
3908            .iter()
3909            .all(|(_, _, predicate)| predicate.failure_message().is_none())
3910        && options.semantics.is_none_or(|semantics| {
3911            semantics.actions.is_empty()
3912                && semantics
3913                    .predicates
3914                    .iter()
3915                    .all(|predicate| predicate.failure_message.is_none())
3916        })
3917        && options.rule_args.is_empty()
3918        && options.member_actions.is_empty()
3919        && options.return_actions.is_empty()
3920        && !atn_has_observable_action_transitions(atn)
3921}
3922
3923#[derive(Clone, Debug, Eq, PartialEq)]
3924struct RecognizeRequest<'a> {
3925    state_number: usize,
3926    stop_state: usize,
3927    index: usize,
3928    rule_start_index: usize,
3929    decision_start_index: Option<usize>,
3930    init_action_rules: &'a BTreeSet<usize>,
3931    predicates: &'a [(usize, usize, ParserPredicate)],
3932    semantics: Option<&'a ParserSemantics>,
3933    rule_args: &'a [ParserRuleArg],
3934    member_actions: &'a [ParserMemberAction],
3935    return_actions: &'a [ParserReturnAction],
3936    local_int_arg: Option<(usize, i64)>,
3937    member_values: BTreeMap<usize, i64>,
3938    return_values: BTreeMap<String, i64>,
3939    rule_alt_number: usize,
3940    track_alt_numbers: bool,
3941    consumed_eof: bool,
3942    /// Current left-recursive precedence threshold, matching ANTLR's
3943    /// `precpred(_ctx, k)` check for generated precedence rules.
3944    precedence: i32,
3945    depth: usize,
3946    recovery_symbols: BTreeSet<i32>,
3947    recovery_state: Option<usize>,
3948}
3949
3950#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
3951struct RecognizeKey {
3952    state_number: usize,
3953    stop_state: usize,
3954    index: usize,
3955    rule_start_index: usize,
3956    decision_start_index: Option<usize>,
3957    local_int_arg: Option<(usize, i64)>,
3958    member_values: BTreeMap<usize, i64>,
3959    return_values: BTreeMap<String, i64>,
3960    rule_alt_number: usize,
3961    track_alt_numbers: bool,
3962    consumed_eof: bool,
3963    precedence: i32,
3964    recovery_symbols: BTreeSet<i32>,
3965    recovery_state: Option<usize>,
3966}
3967
3968#[derive(Clone, Debug, Eq, PartialEq)]
3969struct EpsilonActionStep {
3970    source_state: usize,
3971    target: usize,
3972    action_rule_index: Option<usize>,
3973    left_recursive_boundary: Option<usize>,
3974    decision: Option<usize>,
3975    decision_start_index: Option<usize>,
3976    alt_number: usize,
3977    recovery_symbols: BTreeSet<i32>,
3978    recovery_state: Option<usize>,
3979}
3980
3981struct RecognizeScratch<'a> {
3982    visiting: &'a mut BTreeSet<RecognizeKey>,
3983    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
3984    expected: &'a mut ExpectedTokens,
3985}
3986
3987#[derive(Clone, Debug, Eq, PartialEq)]
3988struct FastRecognizeRequest {
3989    state_number: usize,
3990    stop_state: usize,
3991    index: usize,
3992    rule_start_index: usize,
3993    decision_start_index: Option<usize>,
3994    precedence: i32,
3995    depth: usize,
3996    recovery_symbols: Rc<BTreeSet<i32>>,
3997    recovery_state: Option<usize>,
3998}
3999
4000#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4001struct FastRecognizeTopRequest {
4002    start_state: usize,
4003    stop_state: usize,
4004    start_index: usize,
4005    precedence: i32,
4006    caller_follow_state: Option<usize>,
4007}
4008
4009#[derive(Clone, Copy, Debug)]
4010struct FastPredicateContext<'a> {
4011    predicates: &'a [(usize, usize, ParserPredicate)],
4012    semantics: Option<&'a ParserSemantics>,
4013    member_values: &'a BTreeMap<usize, i64>,
4014}
4015
4016struct FastRecognizeScratch<'a, 'b> {
4017    predicate_context: Option<FastPredicateContext<'a>>,
4018    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4019    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4020    expected: &'b mut ExpectedTokens,
4021}
4022
4023#[derive(Clone, Copy, Debug)]
4024struct FastRepetitionShape {
4025    enter_target: usize,
4026    exit_target: usize,
4027    body_stop_state: usize,
4028    enter_transition_index: usize,
4029    exit_transition_index: usize,
4030}
4031
4032#[derive(Clone, Copy, Debug)]
4033struct FastRepetitionPath {
4034    index: usize,
4035    deferred_nodes: FastDeferredNodeId,
4036    diagnostics: DiagnosticSeqId,
4037    consumed_eof: bool,
4038}
4039
4040enum FastRepetitionWork {
4041    Enter(FastRepetitionPath),
4042    Exit(FastRepetitionPath),
4043}
4044
4045/// Dense entered/exited coordinate sets for one repetition walk.
4046///
4047/// The start coordinate stays inline so short loops avoid a heap allocation;
4048/// later token indexes use one byte each instead of two hash-table entries.
4049struct FastRepetitionCoordinates {
4050    base_index: usize,
4051    base_state: u8,
4052    later_states: Vec<u8>,
4053}
4054
4055impl FastRepetitionCoordinates {
4056    const ENTERED: u8 = 0;
4057    const EXITED: u8 = 2;
4058
4059    const fn new(base_index: usize) -> Self {
4060        Self {
4061            base_index,
4062            base_state: 0,
4063            later_states: Vec::new(),
4064        }
4065    }
4066
4067    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4068        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4069    }
4070
4071    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4072        self.insert(path.index, path.consumed_eof, Self::EXITED)
4073    }
4074
4075    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4076        let Some(offset) = index.checked_sub(self.base_index) else {
4077            return false;
4078        };
4079        let state = if offset == 0 {
4080            &mut self.base_state
4081        } else {
4082            if self.later_states.len() < offset {
4083                self.later_states.resize(offset, 0);
4084            }
4085            &mut self.later_states[offset - 1]
4086        };
4087        let bit = 1 << (base_bit + u8::from(consumed_eof));
4088        let is_new = *state & bit == 0;
4089        *state |= bit;
4090        is_new
4091    }
4092}
4093
4094fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4095    if state.precedence_rule_decision()
4096        || !matches!(
4097            state.kind(),
4098            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4099        )
4100        || state.transitions().len() != 2
4101    {
4102        return None;
4103    }
4104    let mut enter = None;
4105    let mut exit = None;
4106    for (index, transition) in state.transitions().iter().enumerate() {
4107        if transition.kind() != ParserTransitionKind::Epsilon {
4108            return None;
4109        }
4110        let target = transition.target();
4111        if atn
4112            .state(target)
4113            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4114        {
4115            if exit.replace((index, target)).is_some() {
4116                return None;
4117            }
4118        } else if enter.replace((index, target)).is_some() {
4119            return None;
4120        }
4121    }
4122    let (enter_transition_index, enter_target) = enter?;
4123    let (exit_transition_index, exit_target) = exit?;
4124    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4125        atn.state(exit_target)?.loop_back_state()?
4126    } else {
4127        state.state_number()
4128    };
4129    Some(FastRepetitionShape {
4130        enter_target,
4131        exit_target,
4132        body_stop_state,
4133        enter_transition_index,
4134        exit_transition_index,
4135    })
4136}
4137
4138fn push_fast_repetition_work(
4139    work: &mut Vec<FastRepetitionWork>,
4140    shape: FastRepetitionShape,
4141    path: FastRepetitionPath,
4142    lookahead: Option<&DecisionLookahead>,
4143    symbol: i32,
4144) {
4145    // Match the normal recognizer's FIRST-set pruning before queueing work.
4146    // Ambiguous body paths still share the coordinate bitmap below.
4147    let transition_is_viable = |transition_index: usize| {
4148        let Some(entry) = lookahead else {
4149            return true;
4150        };
4151        let Some(transition) = entry.transitions.get(transition_index) else {
4152            return true;
4153        };
4154        transition.nullable || transition.symbols.contains(symbol)
4155    };
4156    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4157    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4158    if shape.enter_transition_index < shape.exit_transition_index {
4159        if exit_is_viable {
4160            work.push(FastRepetitionWork::Exit(path));
4161        }
4162        if enter_is_viable {
4163            work.push(FastRepetitionWork::Enter(path));
4164        }
4165    } else {
4166        if enter_is_viable {
4167            work.push(FastRepetitionWork::Enter(path));
4168        }
4169        if exit_is_viable {
4170            work.push(FastRepetitionWork::Exit(path));
4171        }
4172    }
4173}
4174
4175/// Memo key for the fast recognizer. `recovery_symbols` must come from
4176/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4177/// key, so equal sets share one allocation and the key can store that
4178/// allocation's address instead of cloning an `Rc` and walking the full
4179/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4180/// into distinct cache coordinates.
4181#[derive(Clone, Debug)]
4182struct FastRecognizeKey {
4183    state_number: usize,
4184    stop_state: usize,
4185    index: usize,
4186    rule_start_index: usize,
4187    decision_start_index: Option<usize>,
4188    precedence: i32,
4189    recovery_symbols_id: usize,
4190    recovery_state: Option<usize>,
4191}
4192
4193impl PartialEq for FastRecognizeKey {
4194    fn eq(&self, other: &Self) -> bool {
4195        if self.state_number != other.state_number
4196            || self.stop_state != other.stop_state
4197            || self.index != other.index
4198            || self.rule_start_index != other.rule_start_index
4199            || self.decision_start_index != other.decision_start_index
4200            || self.precedence != other.precedence
4201            || self.recovery_state != other.recovery_state
4202            || self.recovery_symbols_id != other.recovery_symbols_id
4203        {
4204            return false;
4205        }
4206        true
4207    }
4208}
4209
4210impl Eq for FastRecognizeKey {}
4211
4212impl Hash for FastRecognizeKey {
4213    fn hash<H: Hasher>(&self, hasher: &mut H) {
4214        self.state_number.hash(hasher);
4215        self.stop_state.hash(hasher);
4216        self.index.hash(hasher);
4217        self.rule_start_index.hash(hasher);
4218        self.decision_start_index.hash(hasher);
4219        self.precedence.hash(hasher);
4220        self.recovery_state.hash(hasher);
4221        self.recovery_symbols_id.hash(hasher);
4222    }
4223}
4224
4225struct FastRecoveryRequest<'a, 'b> {
4226    atn: &'a Atn,
4227    transition: ParserTransition<'a>,
4228    expected_symbols: Rc<BTreeSet<i32>>,
4229    target: usize,
4230    request: FastRecognizeRequest,
4231    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4232    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4233    expected: &'b mut ExpectedTokens,
4234}
4235
4236struct FastCurrentTokenDeletionRequest<'a, 'b> {
4237    atn: &'a Atn,
4238    expected_symbols: Rc<BTreeSet<i32>>,
4239    request: FastRecognizeRequest,
4240    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4241    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4242    expected: &'b mut ExpectedTokens,
4243}
4244
4245#[derive(Clone, Copy)]
4246struct FastChildRuleFailureRecoveryRequest<'a> {
4247    atn: &'a Atn,
4248    rule_index: usize,
4249    start_index: usize,
4250    follow_state: usize,
4251    stop_state: usize,
4252    expected: &'a ExpectedTokens,
4253}
4254
4255struct RecoveryRequest<'a, 'b> {
4256    atn: &'a Atn,
4257    transition: ParserTransition<'a>,
4258    expected_symbols: BTreeSet<i32>,
4259    target: usize,
4260    request: RecognizeRequest<'a>,
4261    visiting: &'b mut BTreeSet<RecognizeKey>,
4262    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4263    expected: &'b mut ExpectedTokens,
4264}
4265
4266struct CurrentTokenDeletionRequest<'a, 'b> {
4267    atn: &'a Atn,
4268    expected_symbols: BTreeSet<i32>,
4269    request: RecognizeRequest<'a>,
4270    visiting: &'b mut BTreeSet<RecognizeKey>,
4271    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4272    expected: &'b mut ExpectedTokens,
4273}
4274
4275/// Carries the state needed after the normal token-recovery strategies fail
4276/// for a consuming transition.
4277struct ConsumingFailureFallback<'a> {
4278    atn: &'a Atn,
4279    target: usize,
4280    request: RecognizeRequest<'a>,
4281    symbol: i32,
4282    expected_symbols: BTreeSet<i32>,
4283    decision_start_index: Option<usize>,
4284    decision: Option<usize>,
4285}
4286
4287/// Captures the parent-rule context needed when a called rule fails before it
4288/// can produce a normal outcome.
4289struct ChildRuleFailureRecovery<'a> {
4290    atn: &'a Atn,
4291    rule_index: usize,
4292    start_index: usize,
4293    follow_state: usize,
4294    stop_state: usize,
4295    member_values: BTreeMap<usize, i64>,
4296    expected: &'a ExpectedTokens,
4297}
4298
4299/// Bundles the context needed to evaluate one semantic predicate transition.
4300#[derive(Clone, Copy, Debug)]
4301struct PredicateEval<'a> {
4302    index: usize,
4303    rule_index: usize,
4304    pred_index: usize,
4305    predicates: &'a [(usize, usize, ParserPredicate)],
4306    semantics: Option<&'a ParserSemantics>,
4307    context: Option<&'a ParserRuleContext>,
4308    local_int_arg: Option<(usize, i64)>,
4309    member_values: &'a BTreeMap<usize, i64>,
4310}
4311
4312#[derive(Clone, Copy, Debug)]
4313struct ParserSemanticHookRequest<'a> {
4314    index: usize,
4315    rule_index: usize,
4316    pred_index: usize,
4317    context: Option<&'a ParserRuleContext>,
4318    local_int_arg: Option<(usize, i64)>,
4319    member_values: &'a BTreeMap<usize, i64>,
4320}
4321
4322/// Predicate-evaluation context over the recognizer's speculative state.
4323///
4324/// This sits in the prediction hot loop, so everything is borrowed: member
4325/// state read-only from the current speculative path and the rule name
4326/// straight from recognizer metadata. Predicates are pure by construction
4327/// ([`semir::PExpr`] has no mutating node); statement execution uses
4328/// [`ParserTableSemCtx`] (speculative member/return replay) and
4329/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4330struct ParserSemIrCtx<'a, S, H>
4331where
4332    S: TokenSource,
4333    H: SemanticHooks,
4334{
4335    input: &'a mut CommonTokenStream<S>,
4336    tree_storage: &'a ParseTreeStorage,
4337    semantic_hooks: &'a mut H,
4338    rule_index: usize,
4339    coordinate_index: usize,
4340    rule_name: Option<&'a str>,
4341    context: Option<&'a ParserRuleContext>,
4342    local_int_arg: Option<(usize, i64)>,
4343    member_values: &'a BTreeMap<usize, i64>,
4344    invoked_predicates: &'a mut Vec<(usize, usize)>,
4345    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4346    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4347    /// table path instead of coercing the miss to `false`.
4348    unknown_predicate_policy: UnknownSemanticPolicy,
4349    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4350}
4351
4352impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4353where
4354    S: TokenSource,
4355    H: SemanticHooks,
4356{
4357    type TokenText<'a>
4358        = TokenView<'a>
4359    where
4360        Self: 'a;
4361
4362    fn la(&mut self, offset: isize) -> i64 {
4363        i64::from(self.input.la(offset))
4364    }
4365
4366    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4367        self.input.lt(offset)
4368    }
4369
4370    fn token_index_adjacent(&mut self) -> bool {
4371        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4372            return false;
4373        };
4374        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4375            return false;
4376        };
4377        first + 1 == second
4378    }
4379
4380    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4381        self.context.and_then(|context| {
4382            context
4383                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4384                .next()
4385                .map(crate::tree::RuleNodeView::text)
4386        })
4387    }
4388
4389    fn member(&self, member: usize) -> Option<i64> {
4390        Some(self.member_values.get(&member).copied().unwrap_or_default())
4391    }
4392
4393    fn local_arg(&self) -> Option<i64> {
4394        self.local_int_arg.map(|(_, value)| value)
4395    }
4396
4397    fn column(&self) -> Option<i64> {
4398        None
4399    }
4400
4401    fn token_start_column(&self) -> Option<i64> {
4402        None
4403    }
4404
4405    fn token_text_so_far(&self) -> Option<String> {
4406        None
4407    }
4408
4409    fn hook(&mut self, _hook: HookId) -> bool {
4410        let mut ctx = ParserSemCtx {
4411            input: &mut *self.input,
4412            tree_storage: self.tree_storage,
4413            rule_index: self.rule_index,
4414            coordinate_index: self.coordinate_index,
4415            rule_name: self.rule_name.map(str::to_owned),
4416            context: self.context,
4417            tree: None,
4418            local_int_arg: self.local_int_arg,
4419            member_values: self.member_values,
4420            action: None,
4421        };
4422        match self
4423            .semantic_hooks
4424            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4425        {
4426            Some(result) => result,
4427            // No hook answered this coordinate: fall through to the configured
4428            // policy instead of silently rejecting the alternative, matching the
4429            // legacy table path's dispatch chain (hook → policy).
4430            None => apply_unknown_predicate_policy(
4431                self.unknown_predicate_policy,
4432                self.rule_index,
4433                self.coordinate_index,
4434                self.unknown_predicate_hits,
4435            ),
4436        }
4437    }
4438
4439    fn trace_bool(&mut self, value: bool) -> bool {
4440        let key = (self.rule_index, self.coordinate_index);
4441        if !self.invoked_predicates.contains(&key) {
4442            self.invoked_predicates.push(key);
4443            use std::io::Write as _;
4444            let mut stdout = std::io::stdout().lock();
4445            let _ = writeln!(stdout, "eval={value}");
4446        }
4447        value
4448    }
4449}
4450
4451/// Captures predicate-failure recovery metadata for fail-option predicates.
4452struct PredicateFailureRecovery<'a> {
4453    rule_index: usize,
4454    index: usize,
4455    message: &'a str,
4456    member_values: BTreeMap<usize, i64>,
4457    return_values: BTreeMap<String, i64>,
4458    rule_alt_number: usize,
4459}
4460
4461#[derive(Debug)]
4462enum DirectAdaptiveParseControl {
4463    Fallback(DirectAdaptiveFallback),
4464}
4465
4466#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4467enum DirectAdaptiveFallback {
4468    Action,
4469    InvalidAlt,
4470    LeftRecursiveBoundary,
4471    MissingAtn,
4472    NoTransition,
4473    Predicate,
4474    Prediction,
4475    Precedence,
4476    RuleStop,
4477    SemanticContext,
4478    StepLimit,
4479    TokenMismatch,
4480    UnknownDecision,
4481}
4482
4483type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4484
4485struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4486where
4487    S: TokenSource,
4488    H: SemanticHooks,
4489{
4490    parser: &'sim mut BaseParser<S, H>,
4491    atn: &'atn Atn,
4492    simulator: &'sim mut ParserAtnSimulator<'atn>,
4493    decision_by_state: Vec<Option<usize>>,
4494    steps: usize,
4495}
4496
4497/// Outcome of a generated token / set / not-set match that may recover.
4498///
4499/// Generated parsers append `children` to the current rule context. `consumed_eof`
4500/// reports whether the match actually consumed a real EOF terminal — it is true
4501/// only on a successful match (or single-token deletion that lands on EOF), and
4502/// always false on single-token insertion, which synthesizes a missing token and
4503/// consumes nothing. Generated code feeds this into `finish_rule`'s
4504/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4505/// truly matched, matching ANTLR's `matchedEOF` semantics.
4506#[derive(Clone, Debug, Eq, PartialEq)]
4507pub struct GeneratedMatch {
4508    children: GeneratedMatchChildren,
4509    consumed_eof: bool,
4510}
4511
4512#[derive(Clone, Debug, Eq, PartialEq)]
4513enum GeneratedMatchChildren {
4514    One(ParseTree),
4515    Many(Vec<ParseTree>),
4516}
4517
4518struct GeneratedMatchChildrenIntoIter {
4519    one: Option<ParseTree>,
4520    many: Option<std::vec::IntoIter<ParseTree>>,
4521}
4522
4523impl Iterator for GeneratedMatchChildrenIntoIter {
4524    type Item = ParseTree;
4525
4526    fn next(&mut self) -> Option<Self::Item> {
4527        self.one
4528            .take()
4529            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4530    }
4531}
4532
4533impl GeneratedMatch {
4534    /// Parse-tree children produced by the match (the matched terminal, an
4535    /// error node plus deleted-then-matched terminal, or a single missing-token
4536    /// error node).
4537    #[must_use]
4538    pub fn children(&self) -> &[ParseTree] {
4539        match &self.children {
4540            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4541            GeneratedMatchChildren::Many(children) => children,
4542        }
4543    }
4544
4545    /// Consumes the result, returning the children for appending to the rule
4546    /// context.
4547    #[must_use]
4548    pub fn into_children(self) -> Vec<ParseTree> {
4549        match self.children {
4550            GeneratedMatchChildren::One(child) => vec![child],
4551            GeneratedMatchChildren::Many(children) => children,
4552        }
4553    }
4554
4555    /// Consumes the match without allocating for the common single-child case.
4556    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4557        match self.children {
4558            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4559                one: Some(child),
4560                many: None,
4561            },
4562            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4563                one: None,
4564                many: Some(children.into_iter()),
4565            },
4566        }
4567    }
4568
4569    /// Whether a real EOF terminal was consumed by this match.
4570    #[must_use]
4571    pub const fn consumed_eof(&self) -> bool {
4572        self.consumed_eof
4573    }
4574}
4575
4576impl<S> BaseParser<S, NoSemanticHooks>
4577where
4578    S: TokenSource,
4579{
4580    /// Creates a parser base over a buffered token stream and recognizer
4581    /// metadata.
4582    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4583        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4584    }
4585}
4586
4587impl<S, H> BaseParser<S, H>
4588where
4589    S: TokenSource,
4590    H: SemanticHooks,
4591{
4592    /// Creates a parser base with caller-owned semantic hooks.
4593    pub fn with_semantic_hooks(
4594        input: CommonTokenStream<S>,
4595        data: RecognizerData,
4596        semantic_hooks: H,
4597    ) -> Self {
4598        Self {
4599            input,
4600            tree: ParseTreeStorage::new(),
4601            data,
4602            semantic_hooks,
4603            build_parse_trees: true,
4604            syntax_errors: 0,
4605            report_diagnostic_errors: false,
4606            prediction_mode: PredictionMode::Ll,
4607            prediction_diagnostics: Vec::new(),
4608            reported_prediction_diagnostics: BTreeSet::new(),
4609            generated_parser_diagnostics: Vec::new(),
4610            generated_sync_expected: None,
4611            int_members: BTreeMap::new(),
4612            rule_context_stack: Vec::new(),
4613            rule_context_version: 0,
4614            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4615            pending_invoking_states: Vec::new(),
4616            precedence_stack: vec![0],
4617            invoked_predicates: Vec::new(),
4618            bail_on_error: false,
4619            unknown_predicate_policy: UnknownSemanticPolicy::default(),
4620            unknown_predicate_hits: Vec::new(),
4621            unhandled_action_hits: Vec::new(),
4622            rule_first_set_cache: Vec::new(),
4623            state_expected_cache: FxHashMap::default(),
4624            state_expected_token_cache: FxHashMap::default(),
4625            rule_stop_reach_cache: Vec::new(),
4626            recovery_symbols_intern: FxHashMap::default(),
4627            decision_lookahead_cache: FxHashMap::default(),
4628            ll1_decision_cache: FxHashMap::default(),
4629            fast_predicate_cache: FxHashMap::default(),
4630            empty_cycle_cache: Vec::new(),
4631            empty_cycle_cache_atn: None,
4632            clean_memo_mode: CleanMemoMode::Probe,
4633            clean_memo_probe_seen: FxHashSet::default(),
4634            clean_memo_probe_samples: 0,
4635            clean_memo_probe_repeats: 0,
4636            clean_memo_sparse_samples: 0,
4637            fast_recognize_scratch: FastRecognizeTopScratch::default(),
4638            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
4639            empty_recovery_symbols: Rc::new(BTreeSet::new()),
4640            fast_first_set_prefilter: true,
4641            fast_recovery_enabled: true,
4642            fast_token_nodes_enabled: true,
4643            recognition_arena: RecognitionArena::default(),
4644            last_recognition_arena_root: NodeSeqId::EMPTY,
4645            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
4646        }
4647    }
4648
4649    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
4650        &mut self.input
4651    }
4652
4653    /// Fully resets parser-owned state and rewinds the current token stream.
4654    ///
4655    /// Parser configuration, semantic hooks, learned DFA tables, and
4656    /// grammar-owned member values are retained.
4657    pub fn reset(&mut self) {
4658        self.input.seek(0);
4659        self.tree.reset();
4660        self.data.set_state(-1);
4661        self.syntax_errors = 0;
4662        self.prediction_diagnostics.clear();
4663        self.reported_prediction_diagnostics.clear();
4664        self.generated_parser_diagnostics.clear();
4665        self.generated_sync_expected = None;
4666        self.rule_context_stack.clear();
4667        self.advance_rule_context_version();
4668        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
4669        self.pending_invoking_states.clear();
4670        self.precedence_stack.clear();
4671        self.precedence_stack.push(0);
4672        self.invoked_predicates.clear();
4673        self.unknown_predicate_hits.clear();
4674        self.unhandled_action_hits.clear();
4675        self.reset_per_parse_caches();
4676        self.fast_first_set_prefilter = true;
4677        self.fast_recovery_enabled = true;
4678        self.fast_token_nodes_enabled = self.build_parse_trees;
4679        self.reset_recognition_arena();
4680    }
4681
4682    /// Replaces the buffered token stream and fully resets this parser.
4683    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
4684        self.input = input;
4685        self.reset();
4686    }
4687
4688    /// Installs the policy for predicate coordinates that no translated table
4689    /// entry or user hook resolves.
4690    ///
4691    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
4692    /// but generated recursive-descent rules evaluate predicates directly
4693    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
4694    /// going through those options. Generated parser constructors call this so
4695    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
4696    /// the field at its `AssumeTrue` default and silently accepting an
4697    /// unimplemented hook predicate.
4698    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
4699        self.unknown_predicate_policy = policy;
4700    }
4701
4702    /// Reports any unknown predicate coordinate the generated-direct path
4703    /// recorded under [`UnknownSemanticPolicy::Error`], as an
4704    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
4705    /// after a rule completes so the fail-loud policy surfaces on the
4706    /// generated path the same way the interpreter entry surfaces it.
4707    #[must_use]
4708    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
4709        let error = self.unknown_semantic_error();
4710        self.unknown_predicate_hits.clear();
4711        self.unhandled_action_hits.clear();
4712        error
4713    }
4714
4715    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
4716    ///
4717    /// Generated parsers call this at the true top-level entry so a parser
4718    /// reused after a fail-loud (or recovered) parse starts clean, without
4719    /// clearing hits mid-parse where a generated parent still needs a child's
4720    /// recorded coordinate to survive to the top-level boundary.
4721    pub fn reset_unknown_semantic_hits(&mut self) {
4722        self.unknown_predicate_hits.clear();
4723        self.unhandled_action_hits.clear();
4724    }
4725
4726    /// Returns the token stream owned by this parser.
4727    #[must_use]
4728    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
4729        &self.input
4730    }
4731
4732    /// Returns the token stream for source replacement or in-place re-feeding.
4733    #[must_use]
4734    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
4735        &mut self.input
4736    }
4737
4738    /// Returns the canonical token store referenced by parse trees.
4739    #[must_use]
4740    pub const fn token_store(&self) -> &TokenStore {
4741        self.input.token_store()
4742    }
4743
4744    /// Returns the flat CST storage populated by completed rules.
4745    #[must_use]
4746    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
4747        &self.tree
4748    }
4749
4750    /// Resolves a compact parse-tree ID into a borrowing node view.
4751    #[must_use]
4752    pub fn node(&self, id: NodeId) -> Node<'_> {
4753        self.tree
4754            .node(self.input.token_store(), id)
4755            .expect("parser-produced node ID should remain valid")
4756    }
4757
4758    /// Consumes this parser and returns its token stream.
4759    #[must_use]
4760    pub fn into_token_stream(self) -> CommonTokenStream<S> {
4761        self.input
4762    }
4763
4764    /// Consumes this parser and returns its canonical token store.
4765    #[must_use]
4766    pub fn into_token_store(self) -> TokenStore {
4767        self.input.into_token_store()
4768    }
4769
4770    /// Consumes the parser and pairs its token store and flat CST with `root`.
4771    #[must_use]
4772    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
4773        ParsedFile::new(self.input.into_token_store(), self.tree, root)
4774    }
4775
4776    /// Returns the number of parser syntax errors recorded by committed parse
4777    /// paths so far.
4778    pub const fn number_of_syntax_errors(&self) -> usize {
4779        self.syntax_errors
4780    }
4781
4782    /// Computes reachability and retained-capacity counters for the most recent
4783    /// interpreted-rule recognition arena.
4784    ///
4785    /// The reachability scan is linear in the arena size and is deferred until
4786    /// this instrumentation method is called.
4787    #[must_use]
4788    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
4789        self.recognition_arena.stats(
4790            self.last_recognition_arena_root,
4791            self.last_recognition_arena_diagnostics,
4792        )
4793    }
4794
4795    /// Records a syntax error that generated parser code returns as fatal before
4796    /// it can recover into the current rule context.
4797    pub const fn record_generated_syntax_error(&mut self) {
4798        self.record_syntax_errors(1);
4799    }
4800
4801    const fn record_syntax_errors(&mut self, count: usize) {
4802        self.syntax_errors = self.syntax_errors.saturating_add(count);
4803    }
4804
4805    /// Emits diagnostics buffered by the token stream while generated parser
4806    /// code was fetching lexer tokens directly.
4807    pub fn report_token_source_errors(&mut self) {
4808        report_token_source_errors(&self.input.drain_source_errors());
4809    }
4810
4811    /// Captures generated-parser diagnostics and syntax-error count before a
4812    /// speculative generated rule path.
4813    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
4814        GeneratedDiagnosticsCheckpoint {
4815            diagnostics_len: self.generated_parser_diagnostics.len(),
4816            syntax_errors: self.syntax_errors,
4817            tree: self.tree.checkpoint(),
4818        }
4819    }
4820
4821    /// Restores generated-parser diagnostics after a speculative rule path failed.
4822    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
4823        self.generated_parser_diagnostics
4824            .truncate(marker.diagnostics_len);
4825        self.syntax_errors = marker.syntax_errors;
4826        self.generated_sync_expected = None;
4827        self.tree.rollback(marker.tree);
4828    }
4829
4830    /// Emits diagnostics recorded by committed generated parser recovery.
4831    pub fn report_generated_parser_diagnostics(&mut self) {
4832        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
4833        let token_errors = self.input.drain_source_errors();
4834        report_generated_diagnostics(&parser_diagnostics, &token_errors);
4835    }
4836
4837    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
4838    /// decision code.
4839    pub fn record_generated_ambiguity_diagnostic(
4840        &mut self,
4841        atn: &Atn,
4842        state_number: usize,
4843        start_index: usize,
4844        stop_index: usize,
4845        alts: &[usize],
4846    ) {
4847        if !self.report_diagnostic_errors || alts.len() < 2 {
4848            return;
4849        }
4850        let Some(decision) = atn
4851            .decision_to_state()
4852            .iter()
4853            .position(|candidate| candidate == state_number)
4854        else {
4855            return;
4856        };
4857        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
4858            return;
4859        };
4860        let rule_name = self
4861            .rule_names()
4862            .get(rule_index)
4863            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
4864        let input = display_input_text(&self.input.text(start_index, stop_index));
4865        let alts = alts
4866            .iter()
4867            .map(usize::to_string)
4868            .collect::<Vec<_>>()
4869            .join(", ");
4870        let key = (decision, start_index, format!("{alts}:{input}"));
4871        if !self.reported_prediction_diagnostics.insert(key) {
4872            return;
4873        }
4874        let start_diagnostic = diagnostic_for_token(
4875            self.token_at(start_index),
4876            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
4877        );
4878        let stop_diagnostic = diagnostic_for_token(
4879            self.token_at(stop_index),
4880            format!(
4881                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
4882            ),
4883        );
4884        self.generated_parser_diagnostics.push(start_diagnostic);
4885        self.generated_parser_diagnostics.push(stop_diagnostic);
4886    }
4887
4888    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
4889    /// parser calls to the adaptive simulator.
4890    pub fn record_generated_prediction_diagnostic(
4891        &mut self,
4892        atn: &Atn,
4893        state_number: usize,
4894        prediction: &ParserAtnPrediction,
4895    ) {
4896        let Some(diagnostic) = &prediction.diagnostic else {
4897            return;
4898        };
4899        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
4900            return;
4901        }
4902        let Some(decision) = atn
4903            .decision_to_state()
4904            .iter()
4905            .position(|candidate| candidate == state_number)
4906        else {
4907            return;
4908        };
4909        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
4910            return;
4911        };
4912        let rule_name = self
4913            .rule_names()
4914            .get(rule_index)
4915            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
4916        let attempt_input = display_input_text(
4917            &self
4918                .input
4919                .text(diagnostic.start_index, diagnostic.sll_stop_index),
4920        );
4921        let result_input = display_input_text(
4922            &self
4923                .input
4924                .text(diagnostic.start_index, diagnostic.ll_stop_index),
4925        );
4926        let alts = diagnostic
4927            .conflicting_alts
4928            .iter()
4929            .map(usize::to_string)
4930            .collect::<Vec<_>>()
4931            .join(", ");
4932        let key = (
4933            decision,
4934            diagnostic.start_index,
4935            format!(
4936                "{:?}:{alts}:{attempt_input}:{result_input}",
4937                diagnostic.kind
4938            ),
4939        );
4940        if !self.reported_prediction_diagnostics.insert(key) {
4941            return;
4942        }
4943        let attempt_diagnostic = diagnostic_for_token(
4944            self.token_at(diagnostic.sll_stop_index),
4945            format!(
4946                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
4947            ),
4948        );
4949        self.generated_parser_diagnostics.push(attempt_diagnostic);
4950        let message = match diagnostic.kind {
4951            ParserAtnPredictionDiagnosticKind::Ambiguity => {
4952                // Java's DiagnosticErrorListener is exactOnly by default:
4953                // non-exact ambiguities (default LL mode stopping at the
4954                // first resolvable conflict) report the attempt above but
4955                // suppress the ambiguity line itself.
4956                if !diagnostic.exact {
4957                    return;
4958                }
4959                format!(
4960                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
4961                )
4962            }
4963            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
4964                format!(
4965                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
4966                )
4967            }
4968        };
4969        let result_diagnostic =
4970            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
4971        self.generated_parser_diagnostics.push(result_diagnostic);
4972    }
4973
4974    pub fn la(&self, offset: isize) -> i32 {
4975        self.input.la_token(offset)
4976    }
4977
4978    pub fn consume(&mut self) {
4979        IntStream::consume(&mut self.input);
4980    }
4981
4982    /// Sets a generated integer member value used by target-template tests.
4983    pub fn set_int_member(&mut self, member: usize, value: i64) {
4984        self.int_members.insert(member, value);
4985    }
4986
4987    /// Reads a generated integer member value.
4988    pub fn int_member(&self, member: usize) -> Option<i64> {
4989        self.int_members.get(&member).copied()
4990    }
4991
4992    /// Captures generated integer members before speculative generated parser
4993    /// execution.
4994    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
4995        self.int_members.clone()
4996    }
4997
4998    /// Restores generated integer members after generated parser fallback.
4999    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
5000        self.int_members = members;
5001    }
5002
5003    /// Adds `delta` to a generated integer member and returns the new value.
5004    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5005        let value = self.int_members.entry(member).or_default();
5006        *value += delta;
5007        *value
5008    }
5009
5010    fn token_type_for_id(&self, id: TokenId) -> i32 {
5011        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5012    }
5013
5014    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5015        if self.build_parse_trees {
5016            self.tree.terminal(id)
5017        } else {
5018            NodeId::placeholder()
5019        }
5020    }
5021
5022    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5023        if self.build_parse_trees {
5024            self.tree.error(id)
5025        } else {
5026            NodeId::placeholder()
5027        }
5028    }
5029
5030    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5031        context.set_start_id(id);
5032    }
5033
5034    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5035        context.set_stop_id(id);
5036    }
5037
5038    fn insert_synthetic_token(
5039        &mut self,
5040        token_type: i32,
5041        text: String,
5042        line: usize,
5043        column: usize,
5044    ) -> Result<TokenId, AntlrError> {
5045        self.input
5046            .insert(
5047                TokenSpec::explicit(token_type, text)
5048                    .with_span(usize::MAX, usize::MAX)
5049                    .with_byte_span(0, 0)
5050                    .with_position(line, column),
5051            )
5052            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5053    }
5054
5055    /// Matches and consumes the current token when it has the expected token
5056    /// type.
5057    ///
5058    /// On success the consumed token is wrapped as a terminal parse-tree node.
5059    /// On mismatch the error carries vocabulary display names so diagnostics are
5060    /// stable across literal and symbolic token naming.
5061    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5062        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5063            line: 0,
5064            column: 0,
5065            message: "missing current token".to_owned(),
5066        })?;
5067        let current_type = self.token_type_for_id(current);
5068        if current_type == token_type {
5069            self.consume();
5070            Ok(self.terminal_tree(current))
5071        } else {
5072            Err(AntlrError::MismatchedInput {
5073                expected: self.vocabulary().display_name(token_type),
5074                found: self.vocabulary().display_name(current_type),
5075            })
5076        }
5077    }
5078
5079    /// Matches a token from generated recursive-descent code, including ANTLR's
5080    /// single-token insertion recovery when the active rule context can legally
5081    /// continue at the current input symbol.
5082    pub fn match_token_recovering(
5083        &mut self,
5084        token_type: i32,
5085        follow_state: usize,
5086        atn: &Atn,
5087    ) -> Result<GeneratedMatch, AntlrError> {
5088        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5089            line: 0,
5090            column: 0,
5091            message: "missing current token".to_owned(),
5092        })?;
5093        let current_type = self.token_type_for_id(current);
5094        if current_type == token_type {
5095            self.generated_sync_expected = None;
5096            let consumed_eof = current_type == TOKEN_EOF;
5097            self.consume();
5098            return Ok(GeneratedMatch {
5099                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5100                consumed_eof,
5101            });
5102        }
5103        let mut expected_symbols = BTreeSet::new();
5104        expected_symbols.insert(token_type);
5105        self.recover_generated_match(current, &expected_symbols, follow_state, atn, |symbol| {
5106            symbol == token_type
5107        })
5108    }
5109
5110    pub fn match_set_recovering(
5111        &mut self,
5112        intervals: &[(i32, i32)],
5113        follow_state: usize,
5114        atn: &Atn,
5115    ) -> Result<GeneratedMatch, AntlrError> {
5116        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5117            line: 0,
5118            column: 0,
5119            message: "missing current token".to_owned(),
5120        })?;
5121        let current_type = self.token_type_for_id(current);
5122        if interval_set_contains(intervals, current_type) {
5123            self.generated_sync_expected = None;
5124            let consumed_eof = current_type == TOKEN_EOF;
5125            self.consume();
5126            return Ok(GeneratedMatch {
5127                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5128                consumed_eof,
5129            });
5130        }
5131        let expected_symbols = interval_symbols(intervals);
5132        self.recover_generated_match(current, &expected_symbols, follow_state, atn, |symbol| {
5133            interval_set_contains(intervals, symbol)
5134        })
5135    }
5136
5137    pub fn match_not_set_recovering(
5138        &mut self,
5139        intervals: &[(i32, i32)],
5140        min_vocabulary: i32,
5141        max_vocabulary: i32,
5142        follow_state: usize,
5143        atn: &Atn,
5144    ) -> Result<GeneratedMatch, AntlrError> {
5145        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5146            line: 0,
5147            column: 0,
5148            message: "missing current token".to_owned(),
5149        })?;
5150        let current_type = self.token_type_for_id(current);
5151        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5152            && !interval_set_contains(intervals, current_type)
5153        {
5154            self.generated_sync_expected = None;
5155            let consumed_eof = current_type == TOKEN_EOF;
5156            self.consume();
5157            return Ok(GeneratedMatch {
5158                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5159                consumed_eof,
5160            });
5161        }
5162        let expected_symbols =
5163            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5164        self.recover_generated_match(current, &expected_symbols, follow_state, atn, |symbol| {
5165            (min_vocabulary..=max_vocabulary).contains(&symbol)
5166                && !interval_set_contains(intervals, symbol)
5167        })
5168    }
5169
5170    fn recover_generated_match(
5171        &mut self,
5172        current: TokenId,
5173        expected_symbols: &BTreeSet<i32>,
5174        follow_state: usize,
5175        atn: &Atn,
5176        matches: impl Fn(i32) -> bool,
5177    ) -> Result<GeneratedMatch, AntlrError> {
5178        let expected_display = self.expected_symbols_display(expected_symbols);
5179        let (current_type, current_line, current_column, current_display) = {
5180            let token = self
5181                .input
5182                .token_view(current)
5183                .expect("current token ID should be valid");
5184            (
5185                token.token_type(),
5186                token.line(),
5187                token.column(),
5188                token_input_display(&token),
5189            )
5190        };
5191        if self.bail_on_error {
5192            return Err(AntlrError::ParserError {
5193                line: current_line,
5194                column: current_column,
5195                message: format!("mismatched input {current_display} expecting {expected_display}"),
5196            });
5197        }
5198        if current_type != TOKEN_EOF
5199            && let Some(next) = self.input.lt_id(2)
5200            && matches(self.token_type_for_id(next))
5201        {
5202            let message =
5203                format!("extraneous input {current_display} expecting {expected_display}");
5204            self.push_generated_parser_diagnostic(ParserDiagnostic {
5205                line: current_line,
5206                column: current_column,
5207                message,
5208            });
5209            self.record_syntax_errors(1);
5210            self.generated_sync_expected = None;
5211            // Single-token deletion: skip `current`, then accept `next`. The
5212            // accepted token can be EOF only if it is a real EOF terminal.
5213            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5214            self.consume();
5215            self.consume();
5216            return Ok(GeneratedMatch {
5217                children: GeneratedMatchChildren::Many(vec![
5218                    self.error_tree(current),
5219                    self.terminal_tree(next),
5220                ]),
5221                consumed_eof,
5222            });
5223        }
5224        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5225        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5226        // *after* the current element can consume the current symbol. At EOF that
5227        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5228        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5229        // when EOF merely leaks in from the empty enclosing context (as in
5230        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5231        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5232        // so consult the follow state's OWN expected set for the explicit case.
5233        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5234            && self
5235                .cached_state_expected_symbols(atn, follow_state)
5236                .contains(&TOKEN_EOF);
5237        if follow_symbols.contains(&current_type)
5238            && (current_type != TOKEN_EOF
5239                || self.rule_context_stack.len() > 1
5240                || expected_symbols.is_empty()
5241                || follow_explicitly_expects_eof)
5242        {
5243            let message = format!("missing {expected_display} at {current_display}");
5244            self.push_generated_parser_diagnostic(ParserDiagnostic {
5245                line: current_line,
5246                column: current_column,
5247                message,
5248            });
5249            self.record_syntax_errors(1);
5250            self.generated_sync_expected = None;
5251            let token_type = expected_symbols.iter().next().copied().unwrap_or(TOKEN_EOF);
5252            let mut missing_symbol = BTreeSet::new();
5253            missing_symbol.insert(token_type);
5254            let missing_display = self.expected_symbols_display(&missing_symbol);
5255            let token = self.insert_synthetic_token(
5256                token_type,
5257                format!("<missing {missing_display}>"),
5258                current_line,
5259                current_column,
5260            )?;
5261            // Single-token insertion synthesizes a missing token and consumes
5262            // nothing, so no EOF terminal is consumed even when the lookahead is
5263            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5264            // from recording EOF as the rule stop on this recovery path.
5265            return Ok(GeneratedMatch {
5266                children: GeneratedMatchChildren::One(self.error_tree(token)),
5267                consumed_eof: false,
5268            });
5269        }
5270        let mismatch_expected = self.generated_sync_expected.take().map_or_else(
5271            || expected_symbols.clone(),
5272            |symbols| symbols.to_btree_set(),
5273        );
5274        let mismatch_expected_display = self.expected_symbols_display(&mismatch_expected);
5275        Err(AntlrError::ParserError {
5276            line: current_line,
5277            column: current_column,
5278            message: format!(
5279                "mismatched input {current_display} expecting {mismatch_expected_display}"
5280            ),
5281        })
5282    }
5283
5284    fn generated_recovery_follow_symbols(
5285        &mut self,
5286        atn: &Atn,
5287        follow_state: usize,
5288    ) -> BTreeSet<i32> {
5289        let mut follow = self
5290            .cached_state_expected_symbols(atn, follow_state)
5291            .as_ref()
5292            .clone();
5293        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5294            follow.extend(self.context_expected_symbols(atn));
5295        }
5296        follow
5297    }
5298
5299    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5300        self.match_token(TOKEN_EOF)
5301    }
5302
5303    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5304        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5305    }
5306
5307    pub fn match_not_set(
5308        &mut self,
5309        intervals: &[(i32, i32)],
5310        min_vocabulary: i32,
5311        max_vocabulary: i32,
5312    ) -> Result<ParseTree, AntlrError> {
5313        self.match_interval_condition(intervals, |symbol| {
5314            (min_vocabulary..=max_vocabulary).contains(&symbol)
5315                && !interval_set_contains(intervals, symbol)
5316        })
5317    }
5318
5319    fn match_interval_condition(
5320        &mut self,
5321        intervals: &[(i32, i32)],
5322        matches: impl FnOnce(i32) -> bool,
5323    ) -> Result<ParseTree, AntlrError> {
5324        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5325            line: 0,
5326            column: 0,
5327            message: "missing current token".to_owned(),
5328        })?;
5329        let current_type = self.token_type_for_id(current);
5330        if matches(current_type) {
5331            self.consume();
5332            Ok(self.terminal_tree(current))
5333        } else {
5334            Err(AntlrError::MismatchedInput {
5335                expected: self.interval_display(intervals),
5336                found: self.vocabulary().display_name(current_type),
5337            })
5338        }
5339    }
5340
5341    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5342        let values = intervals
5343            .iter()
5344            .map(|(start, stop)| {
5345                if start == stop {
5346                    self.vocabulary().display_name(*start)
5347                } else {
5348                    format!(
5349                        "{}..{}",
5350                        self.vocabulary().display_name(*start),
5351                        self.vocabulary().display_name(*stop)
5352                    )
5353                }
5354            })
5355            .collect::<Vec<_>>()
5356            .join(", ");
5357        format!("{{{values}}}")
5358    }
5359
5360    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5361        if self.build_parse_trees {
5362            self.tree.finish_rule(context)
5363        } else {
5364            NodeId::placeholder()
5365        }
5366    }
5367
5368    /// Enters a generated parser rule and returns the context object the
5369    /// generated method should populate.
5370    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
5371        self.set_state(state);
5372        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
5373        self.rule_context_stack.push(RuleContextFrame {
5374            rule_index,
5375            invoking_state,
5376        });
5377        self.advance_rule_context_version();
5378        let start_index = self.current_visible_index();
5379        let mut context = ParserRuleContext::new(rule_index, invoking_state);
5380        if let Some(token) = self.token_id_at(start_index) {
5381            self.set_context_start(&mut context, token);
5382        }
5383        context
5384    }
5385
5386    /// Records the ATN source state for the next generated rule invocation.
5387    ///
5388    /// ANTLR's full-context prediction reconstructs caller follow states from
5389    /// each active rule context's invoking state. Generated Rust rule methods are
5390    /// plain functions, so the caller supplies that ATN state just before making a
5391    /// rule call; `enter_rule` consumes it when the callee starts.
5392    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
5393        let marker = self.pending_invoking_states.len();
5394        self.pending_invoking_states.push(invoking_state);
5395        marker
5396    }
5397
5398    /// Discards an invoking-state marker if the callee did not consume it.
5399    pub fn discard_invoking_state(&mut self, marker: usize) {
5400        self.pending_invoking_states.truncate(marker);
5401    }
5402
5403    /// Exits the current generated parser rule.
5404    pub fn exit_rule(&mut self) {
5405        self.rule_context_stack.pop();
5406        self.advance_rule_context_version();
5407    }
5408
5409    /// Returns caller follow states for interning in a parser ATN simulator's
5410    /// prediction store. States are yielded outermost to innermost.
5411    pub fn prediction_context_return_states<'a>(
5412        &'a self,
5413        atn: &'a Atn,
5414    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
5415        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
5416            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
5417                return None;
5418            };
5419            let Some(Transition::Rule { follow_state, .. }) = atn
5420                .state(state_number)
5421                .and_then(|state| state.transitions().first())
5422                .map(ParserTransition::data)
5423            else {
5424                return None;
5425            };
5426            Some(follow_state)
5427        })
5428    }
5429
5430    /// Returns a generation that changes whenever the active rule stack changes.
5431    ///
5432    /// A parser ATN simulator uses this to reuse an interned outer prediction
5433    /// context while generated predictions remain in the same rule context.
5434    pub const fn rule_context_version(&self) -> usize {
5435        self.rule_context_version
5436    }
5437
5438    const fn advance_rule_context_version(&mut self) {
5439        self.rule_context_version = self.rule_context_version.wrapping_add(1);
5440    }
5441
5442    /// Adds a generated parser child only when parse-tree construction is
5443    /// enabled. The match is recorded on the context either way (via `add_child`,
5444    /// or `note_matched_child` when trees are off) so generated recovery can tell
5445    /// whether the rule has matched anything yet without depending on `children`.
5446    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
5447        if self.build_parse_trees {
5448            self.tree.add_child(context, child);
5449        } else {
5450            context.note_matched_child();
5451        }
5452    }
5453
5454    fn release_tree_scratch_if_idle(&mut self) {
5455        if self.rule_context_stack.is_empty() {
5456            self.tree.release_scratch();
5457        }
5458    }
5459
5460    /// Finishes a generated parser rule and returns its parse-tree node.
5461    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
5462        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5463        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5464            self.set_context_stop(&mut context, token);
5465        }
5466        let node = self.rule_node(context);
5467        self.exit_rule();
5468        self.release_tree_scratch_if_idle();
5469        node
5470    }
5471
5472    /// Recovers a generated rule catch block after a committed mismatch.
5473    ///
5474    /// ANTLR's generated parsers catch recognition errors inside each rule,
5475    /// report the original error, then consume unexpected tokens until the
5476    /// caller's recovery set can resume. Tokens consumed during recovery become
5477    /// error nodes in the current rule context.
5478    pub fn recover_generated_rule(
5479        &mut self,
5480        context: &mut ParserRuleContext,
5481        atn: &Atn,
5482        error: AntlrError,
5483    ) {
5484        let diagnostic = self.generated_rule_error_diagnostic(error);
5485        self.push_generated_parser_diagnostic(diagnostic);
5486        self.generated_sync_expected = None;
5487        let recovery_symbols = self.context_expected_symbols(atn);
5488        loop {
5489            let symbol = self.la(1);
5490            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
5491                break;
5492            }
5493            let Some(token) = self.input.lt_id(1) else {
5494                break;
5495            };
5496            self.consume();
5497            let child = self.error_tree(token);
5498            self.add_parse_child(context, child);
5499        }
5500        self.record_syntax_errors(1);
5501    }
5502
5503    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
5504        if self
5505            .generated_parser_diagnostics
5506            .iter()
5507            .any(|existing| existing == &diagnostic)
5508        {
5509            return;
5510        }
5511        self.generated_parser_diagnostics.push(diagnostic);
5512    }
5513
5514    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
5515        match error {
5516            AntlrError::ParserError {
5517                line,
5518                column,
5519                message,
5520            } => ParserDiagnostic {
5521                line,
5522                column,
5523                message,
5524            },
5525            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
5526                self.input.lt(1),
5527                format!("mismatched input {found} expecting {expected}"),
5528            ),
5529            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
5530                self.input.lt(1),
5531                format!("no viable alternative at input {input}"),
5532            ),
5533            AntlrError::LexerError {
5534                line,
5535                column,
5536                message,
5537            } => ParserDiagnostic {
5538                line,
5539                column,
5540                message,
5541            },
5542            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
5543        }
5544    }
5545
5546    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
5547    pub fn finish_recursion_rule(
5548        &mut self,
5549        mut context: ParserRuleContext,
5550        consumed_eof: bool,
5551    ) -> ParseTree {
5552        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5553        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5554            self.set_context_stop(&mut context, token);
5555        }
5556        let node = self.rule_node(context);
5557        self.unroll_recursion_context();
5558        self.release_tree_scratch_if_idle();
5559        node
5560    }
5561
5562    /// Enters a generated left-recursive rule at `precedence`.
5563    pub fn enter_recursion_rule(
5564        &mut self,
5565        state: isize,
5566        rule_index: usize,
5567        precedence: i32,
5568    ) -> ParserRuleContext {
5569        self.precedence_stack.push(precedence);
5570        self.enter_rule(state, rule_index)
5571    }
5572
5573    /// Replaces the current context while expanding a left-recursive rule.
5574    pub fn push_new_recursion_context(
5575        &mut self,
5576        state: isize,
5577        rule_index: usize,
5578    ) -> ParserRuleContext {
5579        self.set_state(state);
5580        ParserRuleContext::new(rule_index, state)
5581    }
5582
5583    /// Wraps the previous left-recursive context before parsing the next
5584    /// recursive operator alternative.
5585    pub fn push_new_recursion_context_with_previous(
5586        &mut self,
5587        state: isize,
5588        rule_index: usize,
5589        current: &mut ParserRuleContext,
5590    ) {
5591        self.set_state(state);
5592        if let Some(stop) = self
5593            .rule_stop_token_index(self.input.index(), false)
5594            .and_then(|index| self.token_id_at(index))
5595        {
5596            self.set_context_stop(current, stop);
5597        }
5598        let invoking_state = current.invoking_state();
5599        let start = current.start_id();
5600        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
5601        if start.is_some() {
5602            replacement.set_start_from_context(current);
5603        }
5604        let previous = std::mem::replace(current, replacement);
5605        if self.build_parse_trees {
5606            let previous = self.rule_node(previous);
5607            self.tree.add_child(current, previous);
5608        }
5609    }
5610
5611    /// Leaves a generated left-recursive rule.
5612    pub fn unroll_recursion_context(&mut self) {
5613        if self.precedence_stack.len() > 1 {
5614            self.precedence_stack.pop();
5615        }
5616        self.exit_rule();
5617    }
5618
5619    /// Predicts a generated left-recursive loop from one-token lookahead.
5620    ///
5621    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
5622    /// `None` means caller overlap, a dangerous multi-token prefix, or an
5623    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
5624    /// prediction (which includes the exit alt and precedence filtering).
5625    ///
5626    /// Single-token operators and multi-token prefixes that do not shadow a
5627    /// lower-precedence single-token operator keep the one-token enter fast path.
5628    ///
5629    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
5630    /// operator must not force enter; the adaptive decision may need to select
5631    /// the loop exit instead.
5632    pub fn left_recursive_loop_enter_prediction(
5633        &mut self,
5634        atn: &Atn,
5635        state_number: usize,
5636        precedence: i32,
5637    ) -> Option<bool> {
5638        let symbol = self.la(1);
5639        if symbol == TOKEN_EOF {
5640            return Some(false);
5641        }
5642        let operator_lookahead =
5643            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
5644        let can_single = operator_lookahead.single_token.contains(symbol);
5645        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
5646        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
5647        if !can_single && !can_multi && !can_predicate {
5648            return Some(false);
5649        }
5650        if can_predicate && !can_single {
5651            return None;
5652        }
5653        // Multi-token-only at this precedence, but the same symbol is a
5654        // single-token operator at precedence 0: defer so exit can win when the
5655        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
5656        if !can_single && can_multi && precedence > 0 {
5657            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
5658            if baseline.single_token.contains(symbol) {
5659                return None;
5660            }
5661        }
5662        let atn_key = SharedAtnCacheKey::for_atn(atn);
5663        let cached_overlap = self
5664            .left_recursive_caller_overlap_cache
5665            .iter()
5666            .flatten()
5667            .find(|entry| {
5668                entry.atn_key == atn_key
5669                    && entry.state_number == state_number
5670                    && entry.symbol == symbol
5671                    && entry.context_version == self.rule_context_version
5672            })
5673            .map(|entry| entry.overlaps);
5674        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
5675            let overlaps = caller_context_can_match_symbol_before_state(
5676                atn,
5677                self.prediction_context_return_states(atn),
5678                state_number,
5679                symbol,
5680            );
5681            if let Some(slot) = self
5682                .left_recursive_caller_overlap_cache
5683                .iter_mut()
5684                .find(|slot| slot.is_none())
5685            {
5686                *slot = Some(LeftRecursiveCallerOverlap {
5687                    atn_key,
5688                    state_number,
5689                    symbol,
5690                    context_version: self.rule_context_version,
5691                    overlaps,
5692                });
5693            }
5694            overlaps
5695        });
5696        if caller_overlaps {
5697            return None;
5698        }
5699        Some(true)
5700    }
5701
5702    fn cached_left_recursive_operator_lookahead(
5703        atn: &Atn,
5704        state_number: usize,
5705        precedence: i32,
5706    ) -> Rc<LeftRecursiveOperatorLookahead> {
5707        with_shared_atn_caches(atn, |cache| {
5708            let key = (state_number, precedence);
5709            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
5710                return Rc::clone(cached);
5711            }
5712            let lookahead = Rc::new(left_recursive_operator_lookahead(
5713                atn,
5714                state_number,
5715                precedence,
5716            ));
5717            cache
5718                .left_recursive_operator_lookahead
5719                .insert(key, Rc::clone(&lookahead));
5720            lookahead
5721        })
5722    }
5723
5724    /// Checks whether a generated left-recursive loop can unambiguously enter
5725    /// its operator alternative from one-token lookahead.
5726    pub fn left_recursive_loop_enter_matches(
5727        &mut self,
5728        atn: &Atn,
5729        state_number: usize,
5730        precedence: i32,
5731    ) -> bool {
5732        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
5733    }
5734
5735    /// Implements generated `precpred(_ctx, k)` checks.
5736    pub fn precpred(&self, precedence: i32) -> bool {
5737        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
5738    }
5739
5740    /// Evaluates a generated parser semantic predicate at the current input
5741    /// position.
5742    pub fn parser_semantic_predicate_matches(
5743        &mut self,
5744        predicates: &[(usize, usize, ParserPredicate)],
5745        rule_index: usize,
5746        pred_index: usize,
5747    ) -> bool {
5748        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
5749    }
5750
5751    /// Evaluates a generated parser semantic predicate with the current integer
5752    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
5753    pub fn parser_semantic_predicate_matches_with_local(
5754        &mut self,
5755        predicates: &[(usize, usize, ParserPredicate)],
5756        rule_index: usize,
5757        pred_index: usize,
5758        local_int_arg: i32,
5759    ) -> bool {
5760        self.parser_semantic_predicate_matches_inner(
5761            predicates,
5762            rule_index,
5763            pred_index,
5764            Some((rule_index, i64::from(local_int_arg))),
5765        )
5766    }
5767
5768    fn parser_semantic_predicate_matches_inner(
5769        &mut self,
5770        predicates: &[(usize, usize, ParserPredicate)],
5771        rule_index: usize,
5772        pred_index: usize,
5773        local_int_arg: Option<(usize, i64)>,
5774    ) -> bool {
5775        let index = self.input.index();
5776        let member_values = self.int_members.clone();
5777        self.parser_predicate_matches(PredicateEval {
5778            index,
5779            rule_index,
5780            pred_index,
5781            predicates,
5782            semantics: None,
5783            context: None,
5784            local_int_arg,
5785            member_values: &member_values,
5786        })
5787    }
5788
5789    /// Evaluates a generated parser semantic predicate with access to the
5790    /// current generated rule context.
5791    pub fn parser_semantic_predicate_matches_with_context_and_local(
5792        &mut self,
5793        predicates: &[(usize, usize, ParserPredicate)],
5794        rule_index: usize,
5795        pred_index: usize,
5796        context: &ParserRuleContext,
5797        local_int_arg: i32,
5798    ) -> bool {
5799        let index = self.input.index();
5800        let member_values = self.int_members.clone();
5801        self.parser_predicate_matches(PredicateEval {
5802            index,
5803            rule_index,
5804            pred_index,
5805            predicates,
5806            semantics: None,
5807            context: Some(context),
5808            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
5809            member_values: &member_values,
5810        })
5811    }
5812
5813    /// Evaluates a generated `SemIR` parser predicate with access to the current
5814    /// generated rule context.
5815    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
5816        &mut self,
5817        semantics: &ParserSemantics,
5818        rule_index: usize,
5819        pred_index: usize,
5820        context: &ParserRuleContext,
5821        local_int_arg: i32,
5822    ) -> bool {
5823        let index = self.input.index();
5824        let member_values = self.int_members.clone();
5825        self.parser_predicate_matches(PredicateEval {
5826            index,
5827            rule_index,
5828            pred_index,
5829            predicates: &[],
5830            semantics: Some(semantics),
5831            context: Some(context),
5832            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
5833            member_values: &member_values,
5834        })
5835    }
5836
5837    /// Returns a generated fail-option message for a parser semantic
5838    /// predicate coordinate.
5839    pub fn parser_semantic_predicate_failure_message(
5840        &self,
5841        rule_index: usize,
5842        pred_index: usize,
5843        predicates: &[(usize, usize, ParserPredicate)],
5844    ) -> Option<&'static str> {
5845        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
5846    }
5847
5848    /// Matches any non-EOF token.
5849    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
5850        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5851            line: 0,
5852            column: 0,
5853            message: "missing current token".to_owned(),
5854        })?;
5855        if self.token_type_for_id(current) == TOKEN_EOF {
5856            return Err(AntlrError::MismatchedInput {
5857                expected: "wildcard".to_owned(),
5858                found: self.vocabulary().display_name(TOKEN_EOF),
5859            });
5860        }
5861        self.consume();
5862        Ok(self.terminal_tree(current))
5863    }
5864
5865    /// Generated parser synchronization hook. The current interpreter owns
5866    /// recovery; direct generated methods can call this as a no-op until the
5867    /// generated recovery strategy is expanded.
5868    #[allow(clippy::unnecessary_wraps)]
5869    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
5870        self.set_state(state);
5871        Ok(())
5872    }
5873
5874    /// Synchronizes a generated parser decision against the ATN lookahead set.
5875    ///
5876    /// ANTLR generated parsers call the error strategy before optional and loop
5877    /// decisions. When the current token cannot start any alternative, follow a
5878    /// nullable exit, or be deleted before a later synchronization token, the
5879    /// generated Rust method reports that decision-level mismatch instead of
5880    /// descending into a child rule that cannot start at the current token.
5881    pub fn sync_decision(
5882        &mut self,
5883        atn: &Atn,
5884        state_number: usize,
5885        current_context_empty: bool,
5886        loop_back: bool,
5887    ) -> Result<Vec<ParseTree>, AntlrError> {
5888        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
5889        self.generated_sync_expected = None;
5890        let Some(state) = atn.state(state_number) else {
5891            return Ok(Vec::new());
5892        };
5893        let Some(rule_index) = state.rule_index() else {
5894            return Ok(Vec::new());
5895        };
5896        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
5897            return Ok(Vec::new());
5898        };
5899        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
5900        let symbol = self.la(1);
5901        let mut has_expected_symbols = false;
5902        let mut nullable = false;
5903        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
5904        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
5905        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
5906        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
5907        // and worth deleting; an implicit-follow EOF means the loop should simply
5908        // exit and leave the token for the (absent) caller — matching ANTLR, which
5909        // exits the loop via prediction rather than consuming up to a synthetic EOF.
5910        let mut explicit_eof_expected = false;
5911        for transition in &entry.transitions {
5912            if transition.symbols.contains(symbol) {
5913                return Ok(Vec::new());
5914            }
5915            has_expected_symbols |= !transition.symbols.is_empty();
5916            nullable |= transition.nullable;
5917            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
5918        }
5919        // Happy path: a nullable decision exits when the symbol is in the
5920        // rule-stack follow set. Answer the membership question with an
5921        // early-exit walk; the full union below is only needed for the
5922        // mismatch/deletion diagnostics.
5923        if nullable && self.context_expected_contains(atn, symbol) {
5924            return Ok(Vec::new());
5925        }
5926        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
5927        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
5928            return Ok(Vec::new());
5929        }
5930        let mut expected = TokenBitSet::default();
5931        for transition in &entry.transitions {
5932            expected.extend_from(&transition.symbols);
5933        }
5934        if let Some(context_expected) = context_expected {
5935            expected.extend_from(&context_expected);
5936        }
5937        let can_delete_in_place =
5938            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
5939        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
5940        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
5941        // least one iteration) does `consumeUntil` the follow set — multi-token
5942        // deletion, one error per skipped token across iterations; a loop ENTRY
5943        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
5944        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
5945        // unexpected token only when LA(2) is expected, otherwise report a mismatch
5946        // and leave recovery to the rule.
5947        //
5948        // The generated loop always presents the loop-ENTRY state to this method on
5949        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
5950        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
5951        // an iteration has been taken, and true on a `+` loop's first sync since its
5952        // mandatory first element is iteration 1). Treating a loop entry as a
5953        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
5954        // `c`s, which ANTLR rejects with `mismatched input`).
5955        let loop_sync = loop_back;
5956        if symbol != TOKEN_EOF && can_delete_in_place {
5957            let mut cursor = self.input.index();
5958            let mut skipped = Vec::new();
5959            loop {
5960                let current = self.token_type_at(cursor);
5961                if current == TOKEN_EOF {
5962                    break;
5963                }
5964                skipped.push(cursor);
5965                let next = self.consume_index(cursor, current);
5966                if next == cursor {
5967                    break;
5968                }
5969                let next_symbol = self.token_type_at(next);
5970                // Stop (and delete the skipped tokens as error nodes) when the next
5971                // token is a real expected continuation. EOF counts only when it is
5972                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
5973                // genuinely extraneous and the generated EOF match consumes the real
5974                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
5975                // rule-follow) does NOT count — the loop must exit and leave the
5976                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
5977                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
5978                    explicit_eof_expected
5979                } else {
5980                    expected.contains(next_symbol)
5981                };
5982                if next_is_expected_stop {
5983                    let current_token = self.input.lt(1);
5984                    let expected_symbols = expected.to_btree_set();
5985                    let message = format!(
5986                        "extraneous input {} expecting {}",
5987                        current_token
5988                            .as_ref()
5989                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5990                        self.expected_symbols_display(&expected_symbols)
5991                    );
5992                    self.push_generated_parser_diagnostic(diagnostic_for_token(
5993                        current_token,
5994                        message,
5995                    ));
5996                    self.record_syntax_errors(1);
5997                    let mut children = Vec::with_capacity(skipped.len());
5998                    for index in skipped {
5999                        if let Some(token) = self.token_id_at(index) {
6000                            self.consume();
6001                            children.push(self.error_tree(token));
6002                        }
6003                    }
6004                    return Ok(children);
6005                }
6006                // A non-loop block entry deletes at most one token (single-token
6007                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6008                // is reported at the first token instead of skipping ahead.
6009                if !loop_sync {
6010                    break;
6011                }
6012                cursor = next;
6013            }
6014        }
6015        if nullable {
6016            self.generated_sync_expected = Some(expected);
6017            return Ok(Vec::new());
6018        }
6019        let current = self.input.lt(1);
6020        let expected_symbols = expected.to_btree_set();
6021        Err(AntlrError::ParserError {
6022            line: current.as_ref().map(Token::line).unwrap_or_default(),
6023            column: current.as_ref().map(Token::column).unwrap_or_default(),
6024            message: format!(
6025                "mismatched input {} expecting {}",
6026                current
6027                    .as_ref()
6028                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6029                self.expected_symbols_display(&expected_symbols)
6030            ),
6031        })
6032    }
6033
6034    /// Returns a generated-parser prediction when one token of lookahead
6035    /// uniquely selects an alternative for `state_number`.
6036    ///
6037    /// This mirrors the interpreter's LL(1) commit point and lets generated
6038    /// recursive-descent methods avoid invoking the adaptive simulator for
6039    /// simple optional/block/loop decisions.
6040    pub fn ll1_decision_prediction(
6041        &mut self,
6042        atn: &Atn,
6043        state_number: usize,
6044    ) -> Option<ParserAtnPrediction> {
6045        let state = atn.state(state_number)?;
6046        if state.precedence_rule_decision() {
6047            return None;
6048        }
6049        let rule_stop = state
6050            .rule_index()
6051            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6052        let symbol = self.la(1);
6053        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6054        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6055            alt: alt + 1,
6056            requires_full_context: false,
6057            has_semantic_context: false,
6058            diagnostic: None,
6059        })
6060    }
6061
6062    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6063        let mut expected = BTreeSet::new();
6064        for index in (1..self.rule_context_stack.len()).rev() {
6065            let invoking_state = self.rule_context_stack[index].invoking_state;
6066            let Ok(state_number) = usize::try_from(invoking_state) else {
6067                continue;
6068            };
6069            let Some(Transition::Rule { follow_state, .. }) = atn
6070                .state(state_number)
6071                .and_then(|state| state.transitions().first())
6072                .map(ParserTransition::data)
6073            else {
6074                continue;
6075            };
6076            let return_state = follow_state;
6077            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6078            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
6079                return expected;
6080            }
6081        }
6082        expected.insert(TOKEN_EOF);
6083        expected
6084    }
6085
6086    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6087        let mut expected = TokenBitSet::default();
6088        for index in (1..self.rule_context_stack.len()).rev() {
6089            let invoking_state = self.rule_context_stack[index].invoking_state;
6090            let Ok(state_number) = usize::try_from(invoking_state) else {
6091                continue;
6092            };
6093            let Some(Transition::Rule { follow_state, .. }) = atn
6094                .state(state_number)
6095                .and_then(|state| state.transitions().first())
6096                .map(ParserTransition::data)
6097            else {
6098                continue;
6099            };
6100            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
6101            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6102                return expected;
6103            }
6104        }
6105        expected.insert(TOKEN_EOF);
6106        expected
6107    }
6108
6109    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
6110    /// without materializing the union.
6111    ///
6112    /// This walks the rule-invocation stack directly, innermost frame first —
6113    /// the same frames, in the same order, with the same rule-stop gating as
6114    /// the same outer-context return-state chain used by adaptive prediction.
6115    /// The nullable
6116    /// exit in `sync_decision` asks only this membership question, and on
6117    /// valid input the innermost frame answers it, so the early exit replaces
6118    /// an O(stack-depth) set union per loop/optional exit with one probe.
6119    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
6120        for index in (1..self.rule_context_stack.len()).rev() {
6121            let invoking_state = self.rule_context_stack[index].invoking_state;
6122            let Ok(state_number) = usize::try_from(invoking_state) else {
6123                continue;
6124            };
6125            let Some(Transition::Rule { follow_state, .. }) = atn
6126                .state(state_number)
6127                .and_then(|state| state.transitions().first())
6128                .map(ParserTransition::data)
6129            else {
6130                continue;
6131            };
6132            if self
6133                .cached_state_expected_token_set(atn, follow_state)
6134                .contains(symbol)
6135            {
6136                return true;
6137            }
6138            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6139                return false;
6140            }
6141        }
6142        symbol == TOKEN_EOF
6143    }
6144
6145    /// Builds a generated no-viable-alternative parser error.
6146    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
6147        let error_index = self.input.index();
6148        self.no_viable_alternative_error_at(start_index, error_index)
6149    }
6150
6151    /// Builds a generated no-viable-alternative parser error at the simulator's
6152    /// failing lookahead index. `adaptive_predict` restores the input cursor
6153    /// before returning, so generated parsers have to pass the recorded index
6154    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
6155    pub fn no_viable_alternative_error_at(
6156        &self,
6157        start_index: usize,
6158        error_index: usize,
6159    ) -> AntlrError {
6160        let diagnostic = self.no_viable_alternative(start_index, error_index);
6161        AntlrError::ParserError {
6162            line: diagnostic.line,
6163            column: diagnostic.column,
6164            message: diagnostic.message,
6165        }
6166    }
6167
6168    /// Builds a generated failed-predicate parser error.
6169    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
6170        let current = self.input.lt(1);
6171        AntlrError::ParserError {
6172            line: current.as_ref().map(Token::line).unwrap_or_default(),
6173            column: current.as_ref().map(Token::column).unwrap_or_default(),
6174            message: format!("rule failed predicate: {}", message.into()),
6175        }
6176    }
6177
6178    /// Builds a generated parser error for a semantic predicate with ANTLR's
6179    /// `<fail='...'>` option.
6180    pub fn failed_predicate_option_error(
6181        &self,
6182        rule_index: usize,
6183        message: impl Into<String>,
6184    ) -> AntlrError {
6185        let current = self.input.lt(1);
6186        let rule_name = self
6187            .rule_names()
6188            .get(rule_index)
6189            .map_or_else(|| rule_index.to_string(), Clone::clone);
6190        AntlrError::ParserError {
6191            line: current.as_ref().map(Token::line).unwrap_or_default(),
6192            column: current.as_ref().map(Token::column).unwrap_or_default(),
6193            message: format!("rule {rule_name} {}", message.into()),
6194        }
6195    }
6196
6197    /// Builds a generated parser-action event at the current input position.
6198    pub fn parser_action_at_current(
6199        &mut self,
6200        source_state: usize,
6201        rule_index: usize,
6202        start_index: usize,
6203        consumed_eof: bool,
6204    ) -> ParserAction {
6205        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6206        ParserAction::new(source_state, rule_index, start_index, stop_index)
6207    }
6208
6209    /// Offers a committed parser action event to the user semantic hook.
6210    ///
6211    /// Generated parsers call this for action source states that were present
6212    /// in the ATN but not translated into a built-in Rust action template.
6213    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
6214        let rule_index = action.rule_index();
6215        let rule_name = self.rule_names().get(rule_index).cloned();
6216        let context = None;
6217        let input = &mut self.input;
6218        let semantic_hooks = &mut self.semantic_hooks;
6219        let member_values = &self.int_members;
6220        let mut ctx = ParserSemCtx {
6221            input,
6222            tree_storage: &self.tree,
6223            rule_index,
6224            coordinate_index: usize::MAX,
6225            rule_name,
6226            context,
6227            tree: Some(tree),
6228            local_int_arg: None,
6229            member_values,
6230            action: Some(action),
6231        };
6232        let handled = semantic_hooks.action(&mut ctx, action);
6233        // This action reached the hook because it had no translated arm. If no
6234        // hook handled it either (`SemanticHooks::action` returns `false`), the
6235        // committed action is silently dropped — record it so the parse entry
6236        // can fail loud under the fail-loud boundary, mirroring unknown
6237        // predicates. `assume-*` policies opt out of the fail-loud recording.
6238        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
6239            let coordinate = (rule_index, action.source_state());
6240            if !self.unhandled_action_hits.contains(&coordinate) {
6241                self.unhandled_action_hits.push(coordinate);
6242            }
6243        }
6244        handled
6245    }
6246
6247    /// Attempts to execute a whole generated rule by committing simulator
6248    /// decisions directly. Unsupported constructs or decisions that need
6249    /// full-context / predicate evaluation restore the input cursor and fall
6250    /// back to [`Self::parse_atn_rule`].
6251    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
6252        &mut self,
6253        atn: &'atn Atn,
6254        simulator: &mut ParserAtnSimulator<'atn>,
6255        rule_index: usize,
6256    ) -> Result<ParseTree, AntlrError> {
6257        let start_index = self.current_visible_index();
6258        self.clear_prediction_diagnostics();
6259        self.reset_per_parse_caches();
6260        self.reset_recognition_arena();
6261        let tree_checkpoint = self.tree.checkpoint();
6262        let mut decision_by_state = vec![None; atn.states().len()];
6263        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
6264            if let Some(slot) = decision_by_state.get_mut(state_number) {
6265                *slot = Some(decision);
6266            }
6267        }
6268
6269        let result = DirectAdaptiveParser {
6270            parser: self,
6271            atn,
6272            simulator,
6273            decision_by_state,
6274            steps: 0,
6275        }
6276        .parse_rule(rule_index, -1, 0);
6277
6278        match result {
6279            Ok(tree) => {
6280                report_token_source_errors(&self.input.drain_source_errors());
6281                self.release_tree_scratch_if_idle();
6282                Ok(tree)
6283            }
6284            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
6285                let _ = reason;
6286                self.tree.rollback(tree_checkpoint);
6287                self.input.seek(start_index);
6288                self.parse_atn_rule(atn, rule_index)
6289            }
6290        }
6291    }
6292
6293    /// Parses a generated rule by interpreting the parser ATN from the rule's
6294    /// start state to its stop state.
6295    ///
6296    /// The recognizer backtracks across alternatives and loop exits using token
6297    /// stream indices instead of committing to input consumption immediately.
6298    /// Once a viable ATN path is found, the parser commits the accepted token
6299    /// interval and returns a rule node whose children mirror every grammar
6300    /// rule invocation reached on that path, matching ANTLR's parse-tree
6301    /// shape.
6302    pub fn parse_atn_rule(
6303        &mut self,
6304        atn: &Atn,
6305        rule_index: usize,
6306    ) -> Result<ParseTree, AntlrError> {
6307        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
6308    }
6309
6310    /// Parses a generated rule by interpreting the parser ATN with an initial
6311    /// left-recursive precedence threshold.
6312    pub fn parse_atn_rule_with_precedence(
6313        &mut self,
6314        atn: &Atn,
6315        rule_index: usize,
6316        precedence: i32,
6317    ) -> Result<ParseTree, AntlrError> {
6318        self.parse_atn_rule_with_precedence_inner(atn, rule_index, precedence, None)
6319    }
6320
6321    fn parse_atn_rule_with_precedence_inner(
6322        &mut self,
6323        atn: &Atn,
6324        rule_index: usize,
6325        precedence: i32,
6326        predicate_context: Option<FastPredicateContext<'_>>,
6327    ) -> Result<ParseTree, AntlrError> {
6328        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6329            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6330        })?;
6331        let stop_state = atn
6332            .rule_to_stop_state()
6333            .get(rule_index)
6334            .filter(|state| *state != usize::MAX)
6335            .ok_or_else(|| {
6336                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6337            })?;
6338
6339        let start_index = self.current_visible_index();
6340        self.clear_prediction_diagnostics();
6341        self.reset_per_parse_caches();
6342        self.reset_recognition_arena();
6343        let caller_follow_state = self.pending_invoking_follow_state(atn);
6344        self.fast_recovery_enabled = false;
6345        self.fast_token_nodes_enabled = false;
6346        let top_request = FastRecognizeTopRequest {
6347            start_state,
6348            stop_state,
6349            start_index,
6350            precedence,
6351            caller_follow_state,
6352        };
6353        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
6354        self.fast_token_nodes_enabled = self.build_parse_trees;
6355        let needs_tree_retry = matches!(
6356            &first_pass,
6357            Ok((outcome, _))
6358                if self.build_parse_trees
6359                    && self
6360                        .recognition_arena
6361                        .sequence_has_left_recursive_boundary(outcome.nodes)
6362        );
6363        let needs_retry = match &first_pass {
6364            // The FIRST-set prefilter trims speculative rule calls that can't
6365            // match the current lookahead — useful for perf on grammars with
6366            // many epsilon-reachable rules, but the trim also bypasses
6367            // single-token insertion / deletion recovery that ANTLR's
6368            // reference parser runs at the child rule's first consuming
6369            // transition. Retry without the prefilter whenever the first pass
6370            // either produced no outcome at all or produced a recovered
6371            // outcome (diagnostics non-empty), since the second pass might
6372            // surface a child-level recovery with cleaner diagnostics or
6373            // closer parity to ANTLR's tree shape. Left-recursive tree
6374            // boundaries also need the token-node pass; otherwise the fold has
6375            // no concrete left operand to wrap into ANTLR's recursive context.
6376            Err(_) => true,
6377            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
6378        };
6379        let (outcome, _expected) = if needs_retry {
6380            self.fast_first_set_prefilter = false;
6381            self.fast_recovery_enabled = false;
6382            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6383            let clean_selected = if needs_tree_retry {
6384                match clean_retry {
6385                    ok @ Ok(_) => ok,
6386                    Err(_) => first_pass,
6387                }
6388            } else {
6389                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
6390            };
6391            let selected = if clean_selected.is_err()
6392                || matches!(&clean_selected, Ok((outcome, _)) if !outcome.diagnostics.is_empty())
6393            {
6394                self.fast_recovery_enabled = true;
6395                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6396                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
6397            } else {
6398                clean_selected
6399            };
6400            self.fast_first_set_prefilter = true;
6401            self.fast_recovery_enabled = true;
6402            selected.map_err(|expected| {
6403                if predicate_context.is_some()
6404                    && let Some(error) = self.unknown_semantic_error()
6405                {
6406                    report_token_source_errors(&self.input.drain_source_errors());
6407                    return error;
6408                }
6409                let error = self.recognition_error(rule_index, start_index, &expected);
6410                self.record_syntax_errors(1);
6411                report_token_source_errors(&self.input.drain_source_errors());
6412                error
6413            })?
6414        } else {
6415            first_pass.expect("first_pass is Ok in the no-retry branch")
6416        };
6417        if predicate_context.is_some()
6418            && let Some(error) = self.unknown_semantic_error()
6419        {
6420            report_token_source_errors(&self.input.drain_source_errors());
6421            return Err(error);
6422        }
6423        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6424        report_parser_diagnostics(&self.prediction_diagnostics);
6425        report_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6426        report_token_source_errors(&self.input.drain_source_errors());
6427        let mut context = ParserRuleContext::with_child_capacity(
6428            rule_index,
6429            self.state(),
6430            if self.build_parse_trees {
6431                self.recognition_arena.sequence_len(outcome.nodes)
6432            } else {
6433                0
6434            },
6435        );
6436        if let Some(token) = self.token_id_at(start_index) {
6437            self.set_context_start(&mut context, token);
6438        }
6439        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
6440        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
6441            self.set_context_stop(&mut context, token);
6442        }
6443        let live_root = if self.build_parse_trees {
6444            self.recognition_arena
6445                .fold_left_recursive_boundaries(outcome.nodes)
6446        } else {
6447            outcome.nodes
6448        };
6449        if self.build_parse_trees {
6450            if self
6451                .recognition_arena
6452                .sequence_has_explicit_token(live_root)
6453            {
6454                let mut cursor = live_root;
6455                while let Some(link) = self.recognition_arena.link(cursor) {
6456                    let child = self.arena_recognized_node_tree(link.head, false)?;
6457                    self.tree.add_child(&mut context, child);
6458                    cursor = link.tail;
6459                }
6460            } else {
6461                self.add_arena_implicit_token_children(
6462                    &mut context,
6463                    start_index,
6464                    stop_index,
6465                    live_root,
6466                )?;
6467            }
6468        }
6469        self.finish_recognition_arena(live_root, outcome.diagnostics);
6470        self.input.seek(outcome.index);
6471
6472        let tree = self.rule_node(context);
6473        self.release_tree_scratch_if_idle();
6474        Ok(tree)
6475    }
6476
6477    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
6478        let invoking_state = self.pending_invoking_states.last().copied()?;
6479        let state_number = usize::try_from(invoking_state).ok()?;
6480        match atn.state(state_number)?.transitions().first()?.data() {
6481            Transition::Rule { follow_state, .. } => Some(follow_state),
6482            _ => None,
6483        }
6484    }
6485
6486    #[cfg(test)]
6487    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
6488        caller_follow_token_info_for_stream(&mut self.input, index)
6489    }
6490
6491    /// Runs the fast recognizer once from the rule's start state and returns
6492    /// the best outcome or the per-attempt expected-token accumulator. The
6493    /// caller flips `fast_first_set_prefilter` between calls when a retry is
6494    /// needed, so the FIRST-set cache is left intact across both passes.
6495    fn fast_recognize_top(
6496        &mut self,
6497        atn: &Atn,
6498        request: FastRecognizeTopRequest,
6499        predicate_context: Option<FastPredicateContext<'_>>,
6500    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
6501        let FastRecognizeTopRequest {
6502            start_state,
6503            stop_state,
6504            start_index,
6505            precedence,
6506            caller_follow_state,
6507        } = request;
6508        // `input.size()` is intentionally only the currently buffered token
6509        // count here. Do not restore an up-front fill just to size this map:
6510        // a small floor avoids tiny-input churn, and larger inputs reserve from
6511        // the buffered token count without forcing startup tokenization. The
6512        // 8x multiplier matches the empirical
6513        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
6514        // Kotlin ~12× memo entries per token), so the table avoids one
6515        // rehash on the typical hot path.
6516        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
6517        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
6518        recognize_scratch.prepare(memo_capacity);
6519        let mut expected = ExpectedTokens::default();
6520        let empty_recovery = self.empty_recovery_symbols();
6521        let outcomes = self.recognize_state_fast(
6522            atn,
6523            FastRecognizeRequest {
6524                state_number: start_state,
6525                stop_state,
6526                index: start_index,
6527                rule_start_index: start_index,
6528                decision_start_index: None,
6529                precedence,
6530                depth: 0,
6531                recovery_symbols: empty_recovery,
6532                recovery_state: None,
6533            },
6534            FastRecognizeScratch {
6535                predicate_context,
6536                visiting: &mut recognize_scratch.visiting,
6537                memo: &mut recognize_scratch.memo,
6538                expected: &mut expected,
6539            },
6540        );
6541        recognize_scratch.release_oversized_memo();
6542        self.fast_recognize_scratch = recognize_scratch;
6543        #[cfg(feature = "perf-counters")]
6544        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
6545            perf_counters::dump();
6546            perf_counters::reset();
6547        }
6548        let caller_follow =
6549            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
6550        let selected = {
6551            let arena = &self.recognition_arena;
6552            let input = &mut self.input;
6553            select_best_fast_outcome(
6554                outcomes.into_iter(),
6555                self.prediction_mode,
6556                caller_follow.as_deref(),
6557                |index| caller_follow_token_info_for_stream(input, index),
6558                arena,
6559            )
6560        };
6561        match selected {
6562            Some(mut outcome) => {
6563                if self.build_parse_trees {
6564                    self.materialize_fast_outcome_nodes(&mut outcome);
6565                }
6566                Ok((outcome, expected))
6567            }
6568            None => Err(expected),
6569        }
6570    }
6571
6572    /// Converts one speculative arena record into the flat public CST.
6573    fn arena_recognized_node_tree(
6574        &mut self,
6575        node_id: RecognizedNodeId,
6576        track_alt_numbers: bool,
6577    ) -> Result<ParseTree, AntlrError> {
6578        let node = self.recognition_arena.node(node_id);
6579        match node {
6580            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
6581            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
6582            ArenaRecognizedNode::MissingToken { extra } => {
6583                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
6584                    RecognitionExtra::MissingToken {
6585                        token_type,
6586                        at_index,
6587                        text,
6588                    } => (*token_type, *at_index as usize, text.clone()),
6589                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
6590                        unreachable!("missing-token node must reference missing-token extra")
6591                    }
6592                };
6593                let (line, column) = self
6594                    .token_at(at_index)
6595                    .map_or((0, 0), |token| (token.line(), token.column()));
6596                let token = self.insert_synthetic_token(token_type, text, line, column)?;
6597                Ok(self.error_tree(token))
6598            }
6599            ArenaRecognizedNode::Rule {
6600                rule_index,
6601                invoking_state,
6602                alt_number,
6603                start_index,
6604                stop_index,
6605                return_values,
6606                children,
6607            } => {
6608                let mut context = ParserRuleContext::with_child_capacity(
6609                    rule_index as usize,
6610                    invoking_state as isize,
6611                    self.recognition_arena.sequence_len(children),
6612                );
6613                if track_alt_numbers {
6614                    context.set_alt_number(alt_number as usize);
6615                }
6616                if let Some(extra) = return_values {
6617                    let RecognitionExtra::ReturnValues(values) =
6618                        self.recognition_arena.extra(extra)
6619                    else {
6620                        unreachable!("rule node must reference return-values extra");
6621                    };
6622                    for (name, value) in values {
6623                        context.set_int_return(name.clone(), *value);
6624                    }
6625                }
6626                if let Some(token) = self.token_id_at(start_index as usize) {
6627                    self.set_context_start(&mut context, token);
6628                }
6629                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6630                    self.set_context_stop(&mut context, token);
6631                }
6632                let mut cursor = self
6633                    .recognition_arena
6634                    .fold_left_recursive_boundaries(children);
6635                while let Some(link) = self.recognition_arena.link(cursor) {
6636                    let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
6637                    self.tree.add_child(&mut context, child);
6638                    cursor = link.tail;
6639                }
6640                Ok(self.rule_node(context))
6641            }
6642            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
6643                Err(AntlrError::Unsupported(format!(
6644                    "unfolded left-recursive boundary for rule {rule_index}"
6645                )))
6646            }
6647        }
6648    }
6649
6650    fn arena_recognized_node_tree_with_implicit_tokens(
6651        &mut self,
6652        node_id: RecognizedNodeId,
6653    ) -> Result<ParseTree, AntlrError> {
6654        let node = self.recognition_arena.node(node_id);
6655        match node {
6656            ArenaRecognizedNode::Rule {
6657                rule_index,
6658                invoking_state,
6659                start_index,
6660                stop_index,
6661                children,
6662                ..
6663            } => {
6664                let mut context = ParserRuleContext::with_child_capacity(
6665                    rule_index as usize,
6666                    invoking_state as isize,
6667                    self.recognition_arena.sequence_len(children),
6668                );
6669                if let Some(token) = self.token_id_at(start_index as usize) {
6670                    self.set_context_start(&mut context, token);
6671                }
6672                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6673                    self.set_context_stop(&mut context, token);
6674                }
6675                let children = self
6676                    .recognition_arena
6677                    .fold_left_recursive_boundaries(children);
6678                self.add_arena_implicit_token_children(
6679                    &mut context,
6680                    start_index as usize,
6681                    stop_index.map(|index| index as usize),
6682                    children,
6683                )?;
6684                Ok(self.rule_node(context))
6685            }
6686            _ => self.arena_recognized_node_tree(node_id, false),
6687        }
6688    }
6689
6690    fn add_arena_implicit_token_children(
6691        &mut self,
6692        context: &mut ParserRuleContext,
6693        start_index: usize,
6694        stop_index: Option<usize>,
6695        mut children: NodeSeqId,
6696    ) -> Result<(), AntlrError> {
6697        let mut cursor = Some(start_index);
6698        while let Some(link) = self.recognition_arena.link(children) {
6699            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
6700                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
6701                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6702                self.tree.add_child(context, child);
6703                if let Some(child_stop) = child_stop {
6704                    cursor = self.next_visible_after_token(child_stop);
6705                }
6706            } else {
6707                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6708                self.tree.add_child(context, child);
6709            }
6710            children = link.tail;
6711        }
6712        if let Some(stop) = stop_index {
6713            self.add_visible_terminals_through(context, cursor, stop)?;
6714        }
6715        Ok(())
6716    }
6717
6718    fn add_visible_terminals_before(
6719        &mut self,
6720        context: &mut ParserRuleContext,
6721        cursor: &mut Option<usize>,
6722        before: usize,
6723    ) -> Result<(), AntlrError> {
6724        let Some(stop) = before.checked_sub(1) else {
6725            return Ok(());
6726        };
6727        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
6728        *cursor = next;
6729        Ok(())
6730    }
6731
6732    fn add_visible_terminals_through(
6733        &mut self,
6734        context: &mut ParserRuleContext,
6735        mut cursor: Option<usize>,
6736        stop: usize,
6737    ) -> Result<Option<usize>, AntlrError> {
6738        while let Some(index) = cursor {
6739            if index > stop {
6740                return Ok(Some(index));
6741            }
6742            let token = self
6743                .input
6744                .get_id(index)
6745                .ok_or_else(|| AntlrError::ParserError {
6746                    line: 0,
6747                    column: 0,
6748                    message: format!("missing token at index {index}"),
6749                })?;
6750            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
6751            let child = self.terminal_tree(token);
6752            self.tree.add_child(context, child);
6753            if is_eof {
6754                return Ok(None);
6755            }
6756            cursor = self.next_visible_after_token(index);
6757        }
6758        Ok(None)
6759    }
6760
6761    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
6762        let next = self.input.next_visible_after(index);
6763        (next != index).then_some(next)
6764    }
6765
6766    /// Parses a generated rule and returns semantic actions reached on the
6767    /// selected ATN path.
6768    ///
6769    /// This slower path preserves action ordering and token intervals for
6770    /// generated code that replays target-specific action templates after the
6771    /// recognizer has chosen one viable parse path.
6772    pub fn parse_atn_rule_with_actions(
6773        &mut self,
6774        atn: &Atn,
6775        rule_index: usize,
6776    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6777        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
6778    }
6779
6780    /// Parses a generated rule and emits ATN actions plus selected rule-init
6781    /// actions reached on the chosen path.
6782    ///
6783    /// Generated parsers use this when a grammar contains rule-level `@init`
6784    /// templates that must run for nested rule invocations. The runtime keeps
6785    /// the action list path-sensitive, so init templates are replayed only for
6786    /// rules that were actually entered by the selected parse.
6787    pub fn parse_atn_rule_with_action_inits(
6788        &mut self,
6789        atn: &Atn,
6790        rule_index: usize,
6791        init_action_rules: &[usize],
6792    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6793        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
6794    }
6795
6796    /// Parses a generated rule with optional semantic-action replay features.
6797    ///
6798    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
6799    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
6800    /// unchanged for grammars that do not request that target template.
6801    pub fn parse_atn_rule_with_action_options(
6802        &mut self,
6803        atn: &Atn,
6804        rule_index: usize,
6805        init_action_rules: &[usize],
6806        track_alt_numbers: bool,
6807    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6808        self.parse_atn_rule_with_runtime_options(
6809            atn,
6810            rule_index,
6811            ParserRuntimeOptions {
6812                init_action_rules,
6813                track_alt_numbers,
6814                ..ParserRuntimeOptions::default()
6815            },
6816        )
6817    }
6818
6819    /// Parses a generated rule with action replay and parser predicate support.
6820    ///
6821    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
6822    /// target-template predicate semantics emitted by the generator. Missing
6823    /// entries are treated as true so unsupported predicate-free grammars keep
6824    /// the previous unconditional transition behavior.
6825    pub fn parse_atn_rule_with_runtime_options(
6826        &mut self,
6827        atn: &Atn,
6828        rule_index: usize,
6829        options: ParserRuntimeOptions<'_>,
6830    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6831        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
6832    }
6833
6834    /// Parses a generated rule with action replay, parser predicate support,
6835    /// and an initial left-recursive precedence threshold.
6836    pub fn parse_atn_rule_with_runtime_options_and_precedence(
6837        &mut self,
6838        atn: &Atn,
6839        rule_index: usize,
6840        precedence: i32,
6841        options: ParserRuntimeOptions<'_>,
6842    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6843        let ParserRuntimeOptions {
6844            init_action_rules,
6845            track_alt_numbers,
6846            predicates,
6847            semantics,
6848            rule_args,
6849            member_actions,
6850            return_actions,
6851            unknown_predicate_policy,
6852        } = options;
6853        if init_action_rules.is_empty()
6854            && !track_alt_numbers
6855            && predicates.is_empty()
6856            && semantics.is_none()
6857            && rule_args.is_empty()
6858            && member_actions.is_empty()
6859            && return_actions.is_empty()
6860            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
6861            && !atn_has_observable_action_transitions(atn)
6862            && (!self.semantic_hooks.observes_parser_predicates()
6863                || !atn_has_predicate_transitions(atn))
6864        {
6865            return self
6866                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
6867                .map(|tree| (tree, Vec::new()));
6868        }
6869        if can_use_fast_predicate_recognizer(atn, &options) {
6870            self.unknown_predicate_policy = unknown_predicate_policy;
6871            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
6872            let member_values = self.int_members.clone();
6873            let result = self
6874                .parse_atn_rule_with_precedence_inner(
6875                    atn,
6876                    rule_index,
6877                    precedence,
6878                    Some(FastPredicateContext {
6879                        predicates,
6880                        semantics,
6881                        member_values: &member_values,
6882                    }),
6883                )
6884                .map(|tree| (tree, Vec::new()));
6885            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
6886                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
6887            }
6888            return result;
6889        }
6890        self.unknown_predicate_policy = unknown_predicate_policy;
6891        // A generated parent may have already recorded unknown-predicate
6892        // coordinates before descending into this (interpreted) child. Clearing
6893        // unconditionally would drop them before the parent's public entry
6894        // surfaces them, so stash and restore around this call: recognition sees
6895        // only the hits it records itself (so the fail-loud check below reflects
6896        // this rule), and the parent's prior hits are merged back afterward.
6897        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
6898        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6899            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6900        })?;
6901        let stop_state = atn
6902            .rule_to_stop_state()
6903            .get(rule_index)
6904            .filter(|state| *state != usize::MAX)
6905            .ok_or_else(|| {
6906                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6907            })?;
6908
6909        let start_index = self.current_visible_index();
6910        self.clear_prediction_diagnostics();
6911        self.reset_per_parse_caches();
6912        self.reset_recognition_arena();
6913        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
6914        let invoking_state = self.pending_invoking_states.pop();
6915        let local_int_arg = invoking_state
6916            .and_then(|state| usize::try_from(state).ok())
6917            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
6918        let mut visiting = BTreeSet::new();
6919        let mut memo = BTreeMap::new();
6920        let mut expected = ExpectedTokens::default();
6921        let member_values = self.int_members.clone();
6922        let return_values = BTreeMap::new();
6923        let outcomes = self.recognize_state(
6924            atn,
6925            RecognizeRequest {
6926                state_number: start_state,
6927                stop_state,
6928                index: start_index,
6929                rule_start_index: start_index,
6930                decision_start_index: None,
6931                init_action_rules: &init_action_rules,
6932                predicates,
6933                semantics,
6934                rule_args,
6935                member_actions,
6936                return_actions,
6937                local_int_arg,
6938                member_values,
6939                return_values,
6940                rule_alt_number: 0,
6941                track_alt_numbers,
6942                consumed_eof: false,
6943                precedence,
6944                depth: 0,
6945                recovery_symbols: BTreeSet::new(),
6946                recovery_state: None,
6947            },
6948            &mut visiting,
6949            &mut memo,
6950            &mut expected,
6951        );
6952        if let Some(error) = self.unknown_semantic_error() {
6953            report_token_source_errors(&self.input.drain_source_errors());
6954            // Keep the recorded coordinates: when this interpreted rule is a
6955            // child of a generated parent, the parent's catch block recovers an
6956            // ordinary `AntlrError` into a partial subtree, so the fail-loud
6957            // coordinate must survive on the parser for the top-level entry's
6958            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
6959            // is handled by clearing at the top-level generated entry instead.
6960            return Err(error);
6961        }
6962        // Recognition recorded no unresolved coordinate of its own; merge the
6963        // parent's prior hits back so its public entry can still surface them.
6964        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
6965        let Some(outcome) = select_best_outcome(
6966            outcomes.into_iter(),
6967            self.prediction_mode,
6968            &self.recognition_arena,
6969        ) else {
6970            let error = self.recognition_error(rule_index, start_index, &expected);
6971            self.record_syntax_errors(1);
6972            report_token_source_errors(&self.input.drain_source_errors());
6973            return Err(error);
6974        };
6975
6976        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6977        report_parser_diagnostics(&self.prediction_diagnostics);
6978        report_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6979        report_token_source_errors(&self.input.drain_source_errors());
6980        let mut actions = outcome.actions;
6981        if init_action_rules.contains(&rule_index) {
6982            actions.insert(
6983                0,
6984                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
6985            );
6986        }
6987        let mut context =
6988            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
6989        if track_alt_numbers {
6990            context.set_alt_number(outcome.alt_number);
6991        }
6992        for (name, value) in outcome.return_values {
6993            context.set_int_return(name, value);
6994        }
6995        if let Some(token) = self.token_id_at(start_index) {
6996            self.set_context_start(&mut context, token);
6997        }
6998        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
6999            self.set_context_stop(&mut context, token);
7000        }
7001        let live_root = if self.build_parse_trees {
7002            self.recognition_arena
7003                .fold_left_recursive_boundaries(outcome.nodes)
7004        } else {
7005            outcome.nodes
7006        };
7007        if self.build_parse_trees {
7008            let mut nodes = live_root;
7009            while let Some(link) = self.recognition_arena.link(nodes) {
7010                let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
7011                self.tree.add_child(&mut context, child);
7012                nodes = link.tail;
7013            }
7014        }
7015        self.finish_recognition_arena(live_root, outcome.diagnostics);
7016        self.input.seek(outcome.index);
7017
7018        let tree = self.rule_node(context);
7019        self.release_tree_scratch_if_idle();
7020        Ok((tree, actions))
7021    }
7022
7023    /// Temporary parser entry used by generated parser methods while the parser
7024    /// ATN simulator is being implemented.
7025    ///
7026    /// This keeps generated parser crates buildable and gives us a stable method
7027    /// surface for every grammar rule. It intentionally accepts all remaining
7028    /// tokens into one rule context; it is not the final parser semantics.
7029    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
7030        let mut context = ParserRuleContext::new(rule_index, self.state());
7031        while self.la(1) != TOKEN_EOF {
7032            let token_type = self.la(1);
7033            let child = self.match_token(token_type)?;
7034            if self.build_parse_trees {
7035                self.tree.add_child(&mut context, child);
7036            }
7037        }
7038        if self.build_parse_trees {
7039            let child = self.match_eof()?;
7040            self.tree.add_child(&mut context, child);
7041        }
7042        let tree = self.rule_node(context);
7043        self.release_tree_scratch_if_idle();
7044        Ok(tree)
7045    }
7046
7047    /// Builds the parser error reported when no ATN path can reach the active
7048    /// rule stop state.
7049    fn recognition_error(
7050        &mut self,
7051        rule_index: usize,
7052        start_index: usize,
7053        expected: &ExpectedTokens,
7054    ) -> AntlrError {
7055        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
7056        self.input.seek(index);
7057        let current = self.input.lt(1);
7058        let line = current.as_ref().map(Token::line).unwrap_or_default();
7059        let column = current.as_ref().map(Token::column).unwrap_or_default();
7060        AntlrError::ParserError {
7061            line,
7062            column,
7063            message,
7064        }
7065    }
7066
7067    /// Builds the token index and ANTLR-compatible message for a failed rule.
7068    fn expected_error_message(
7069        &mut self,
7070        rule_index: usize,
7071        start_index: usize,
7072        expected: &ExpectedTokens,
7073    ) -> (usize, String) {
7074        let index = expected
7075            .index
7076            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
7077            .unwrap_or_else(|| self.input.index());
7078        self.input.seek(index);
7079        let current = self.input.lt(1);
7080        let message = if expected
7081            .no_viable
7082            .as_ref()
7083            .is_some_and(|no_viable| no_viable.error_index == index)
7084        {
7085            let start = expected
7086                .no_viable
7087                .as_ref()
7088                .map_or(start_index, |no_viable| no_viable.start_index);
7089            let text = display_input_text(&self.input.text(start, index));
7090            format!("no viable alternative at input '{text}'")
7091        } else if expected.symbols.is_empty() {
7092            if expected.index.is_some() {
7093                let found = current
7094                    .as_ref()
7095                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7096                if current
7097                    .as_ref()
7098                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7099                {
7100                    format!(
7101                        "missing {} at {found}",
7102                        self.expected_symbols_display(&expected.symbols)
7103                    )
7104                } else {
7105                    format!("mismatched input {found}")
7106                }
7107            } else {
7108                format!("no viable alternative while parsing rule {rule_index}")
7109            }
7110        } else {
7111            format!(
7112                "mismatched input {} expecting {}",
7113                current
7114                    .as_ref()
7115                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7116                self.expected_symbols_display(&expected.symbols)
7117            )
7118        };
7119        (index, message)
7120    }
7121
7122    /// Converts a failed child rule into a recovered outcome so the parent can
7123    /// continue after reporting the child diagnostic.
7124    fn child_rule_failure_recovery(
7125        &mut self,
7126        rule_index: usize,
7127        start_index: usize,
7128        sync_symbols: &BTreeSet<i32>,
7129        member_values: BTreeMap<usize, i64>,
7130        expected: &ExpectedTokens,
7131    ) -> Option<RecognizeOutcome> {
7132        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7133        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7134        let mut next_index = error_index;
7135        loop {
7136            let symbol = self.token_type_at(next_index);
7137            if sync_symbols.contains(&symbol) {
7138                if next_index == error_index {
7139                    return None;
7140                }
7141                break;
7142            }
7143            if symbol == TOKEN_EOF {
7144                break;
7145            }
7146            let after = self.consume_index(next_index, symbol);
7147            if after == next_index {
7148                break;
7149            }
7150            next_index = after;
7151        }
7152        let mut nodes = NodeSeqId::EMPTY;
7153        let error = self.arena_token_node(error_index, true);
7154        self.arena_prepend(&mut nodes, error);
7155        let diagnostics = self
7156            .recognition_arena
7157            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7158        Some(RecognizeOutcome {
7159            index: next_index,
7160            consumed_eof: false,
7161            alt_number: 0,
7162            member_values,
7163            return_values: BTreeMap::new(),
7164            diagnostics,
7165            decisions: Vec::new(),
7166            actions: Vec::new(),
7167            nodes,
7168        })
7169    }
7170
7171    /// Adapts the optional recovery result to the normal outcome list used by
7172    /// rule-call transitions.
7173    fn child_rule_failure_recovery_outcomes(
7174        &mut self,
7175        request: ChildRuleFailureRecovery<'_>,
7176    ) -> Vec<RecognizeOutcome> {
7177        let sync_symbols =
7178            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
7179        self.child_rule_failure_recovery(
7180            request.rule_index,
7181            request.start_index,
7182            &sync_symbols,
7183            request.member_values,
7184            request.expected,
7185        )
7186        .into_iter()
7187        .collect()
7188    }
7189
7190    /// Formats expected token types using ANTLR's single-token or set syntax.
7191    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
7192        expected_symbols_display(symbols, self.vocabulary())
7193    }
7194
7195    /// Returns the single-token deletion repair if the token after `index`
7196    /// satisfies the failed consuming transition.
7197    fn single_token_deletion(
7198        &mut self,
7199        transition: ParserTransition<'_>,
7200        index: usize,
7201        max_token_type: i32,
7202        expected_symbols: &BTreeSet<i32>,
7203    ) -> Option<(ParserDiagnostic, usize, i32)> {
7204        let current_symbol = self.token_type_at(index);
7205        if current_symbol == TOKEN_EOF {
7206            return None;
7207        }
7208        let next_index = self.consume_index(index, current_symbol);
7209        if next_index == index {
7210            return None;
7211        }
7212        let next_symbol = self.token_type_at(next_index);
7213        if !transition.matches(next_symbol, 1, max_token_type) {
7214            return None;
7215        }
7216        let transition_expected = transition_expected_symbols(transition, max_token_type);
7217        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7218            &transition_expected
7219        } else {
7220            expected_symbols
7221        });
7222        let current = self.token_at(index);
7223        let message = format!(
7224            "extraneous input {} expecting {expected_display}",
7225            current
7226                .as_ref()
7227                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7228        );
7229        Some((
7230            diagnostic_for_token(current, message),
7231            next_index,
7232            next_symbol,
7233        ))
7234    }
7235
7236    /// Returns the repair used when deleting the current token lets a recovery
7237    /// state continue with the following token.
7238    fn current_token_deletion(
7239        &mut self,
7240        index: usize,
7241        expected_symbols: &BTreeSet<i32>,
7242    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
7243        if expected_symbols.is_empty() {
7244            return None;
7245        }
7246        let current_symbol = self.token_type_at(index);
7247        if current_symbol == TOKEN_EOF {
7248            return None;
7249        }
7250        let current = self.token_at(index);
7251        let message = format!(
7252            "extraneous input {} expecting {}",
7253            current
7254                .as_ref()
7255                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7256            self.expected_symbols_display(expected_symbols)
7257        );
7258        let diagnostic = diagnostic_for_token(current, message);
7259        let mut skipped = Vec::new();
7260        let mut cursor = index;
7261        loop {
7262            let symbol = self.token_type_at(cursor);
7263            if symbol == TOKEN_EOF {
7264                return None;
7265            }
7266            skipped.push(cursor);
7267            let next_index = self.consume_index(cursor, symbol);
7268            if next_index == cursor {
7269                return None;
7270            }
7271            let next_symbol = self.token_type_at(next_index);
7272            if expected_symbols.contains(&next_symbol) {
7273                return Some((diagnostic, next_index, skipped));
7274            }
7275            cursor = next_index;
7276        }
7277    }
7278
7279    /// Returns the single-token insertion repair for a failed consuming
7280    /// transition. The caller validates the repair by continuing from the
7281    /// transition target at the same input index.
7282    fn single_token_insertion(
7283        &mut self,
7284        transition: ParserTransition<'_>,
7285        index: usize,
7286        max_token_type: i32,
7287        expected_symbols: &BTreeSet<i32>,
7288        follow_symbols: &BTreeSet<i32>,
7289    ) -> Option<(ParserDiagnostic, i32, String)> {
7290        let current_symbol = self.token_type_at(index);
7291        if !follow_symbols.contains(&current_symbol) {
7292            return None;
7293        }
7294        let transition_expected = transition_expected_symbols(transition, max_token_type);
7295        let token_type = transition_expected.iter().next().copied()?;
7296        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7297            &transition_expected
7298        } else {
7299            expected_symbols
7300        });
7301        let mut token_symbols = BTreeSet::new();
7302        token_symbols.insert(token_type);
7303        let missing_token_display = self.expected_symbols_display(&token_symbols);
7304        let current = self.token_at(index);
7305        let message = format!(
7306            "missing {expected_display} at {}",
7307            current
7308                .as_ref()
7309                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7310        );
7311        let text = format!("<missing {missing_token_display}>");
7312        Some((
7313            diagnostic_for_token(current.as_ref(), message),
7314            token_type,
7315            text,
7316        ))
7317    }
7318
7319    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
7320    /// skip the unexpected current token when the following token satisfies the
7321    /// transition that failed.
7322    fn fast_single_token_deletion_recovery(
7323        &mut self,
7324        recovery: FastRecoveryRequest<'_, '_>,
7325        predicate_context: Option<FastPredicateContext<'_>>,
7326    ) -> Vec<FastRecognizeOutcome> {
7327        let FastRecoveryRequest {
7328            atn,
7329            transition,
7330            expected_symbols,
7331            target,
7332            request,
7333            visiting,
7334            memo,
7335            expected,
7336        } = recovery;
7337        let FastRecognizeRequest {
7338            stop_state,
7339            index,
7340            rule_start_index,
7341            decision_start_index,
7342            precedence,
7343            depth,
7344            ..
7345        } = request;
7346        let Some((diagnostic, next_index, next_symbol)) =
7347            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7348        else {
7349            return Vec::new();
7350        };
7351        let after_next = self.consume_index(next_index, next_symbol);
7352        let empty_recovery = self.empty_recovery_symbols();
7353        self.recognize_state_fast(
7354            atn,
7355            FastRecognizeRequest {
7356                state_number: target,
7357                stop_state,
7358                index: after_next,
7359                rule_start_index,
7360                decision_start_index,
7361                precedence,
7362                depth: depth + 1,
7363                recovery_symbols: empty_recovery,
7364                recovery_state: None,
7365            },
7366            FastRecognizeScratch {
7367                predicate_context,
7368                visiting,
7369                memo,
7370                expected,
7371            },
7372        )
7373        .into_iter()
7374        .map(|mut outcome| {
7375            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7376            outcome.diagnostics = self
7377                .recognition_arena
7378                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7379            if self.fast_token_nodes_enabled {
7380                let token = self.arena_token_node(next_index, false);
7381                self.defer_fast_outcome_node(&mut outcome, token);
7382                let error = self.arena_token_node(index, true);
7383                self.defer_fast_outcome_node(&mut outcome, error);
7384            }
7385            outcome
7386        })
7387        .collect()
7388    }
7389
7390    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
7391    /// pretend the expected transition token was present and continue without
7392    /// consuming the current token.
7393    fn fast_single_token_insertion_recovery(
7394        &mut self,
7395        recovery: FastRecoveryRequest<'_, '_>,
7396        predicate_context: Option<FastPredicateContext<'_>>,
7397    ) -> Vec<FastRecognizeOutcome> {
7398        let FastRecoveryRequest {
7399            atn,
7400            transition,
7401            expected_symbols,
7402            target,
7403            request,
7404            visiting,
7405            memo,
7406            expected,
7407        } = recovery;
7408        let FastRecognizeRequest {
7409            stop_state,
7410            index,
7411            rule_start_index,
7412            decision_start_index,
7413            precedence,
7414            depth,
7415            ..
7416        } = request;
7417        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
7418        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7419            transition,
7420            index,
7421            atn.max_token_type(),
7422            &expected_symbols,
7423            &follow_symbols,
7424        ) else {
7425            return Vec::new();
7426        };
7427        let empty_recovery = self.empty_recovery_symbols();
7428        self.recognize_state_fast(
7429            atn,
7430            FastRecognizeRequest {
7431                state_number: target,
7432                stop_state,
7433                index,
7434                rule_start_index,
7435                decision_start_index,
7436                precedence,
7437                depth: depth + 1,
7438                recovery_symbols: empty_recovery,
7439                recovery_state: None,
7440            },
7441            FastRecognizeScratch {
7442                predicate_context,
7443                visiting,
7444                memo,
7445                expected,
7446            },
7447        )
7448        .into_iter()
7449        .map(|mut outcome| {
7450            outcome.diagnostics = self
7451                .recognition_arena
7452                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7453            let missing = self.arena_missing_token_node(token_type, index, text.clone());
7454            self.defer_fast_outcome_node(&mut outcome, missing);
7455            outcome
7456        })
7457        .collect()
7458    }
7459
7460    /// Retries the current fast-recognition state after deleting one
7461    /// unexpected token that precedes a valid loop or block continuation.
7462    fn fast_current_token_deletion_recovery(
7463        &mut self,
7464        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
7465        predicate_context: Option<FastPredicateContext<'_>>,
7466    ) -> Vec<FastRecognizeOutcome> {
7467        let FastCurrentTokenDeletionRequest {
7468            atn,
7469            expected_symbols,
7470            mut request,
7471            visiting,
7472            memo,
7473            expected,
7474        } = recovery;
7475        if request.index == request.rule_start_index {
7476            return Vec::new();
7477        }
7478        let Some((diagnostic, next_index, skipped)) =
7479            self.current_token_deletion(request.index, &expected_symbols)
7480        else {
7481            return Vec::new();
7482        };
7483        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7484        request.index = next_index;
7485        request.depth += 1;
7486        request.recovery_state = None;
7487        self.recognize_state_fast(
7488            atn,
7489            request,
7490            FastRecognizeScratch {
7491                predicate_context,
7492                visiting,
7493                memo,
7494                expected,
7495            },
7496        )
7497        .into_iter()
7498        .map(|mut outcome| {
7499            outcome.diagnostics = self
7500                .recognition_arena
7501                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7502            for index in skipped.iter().rev() {
7503                let error = self.arena_token_node(*index, true);
7504                self.defer_fast_outcome_node(&mut outcome, error);
7505            }
7506            outcome
7507        })
7508        .collect()
7509    }
7510
7511    /// Converts a failed child rule into a recovered fast-recognizer outcome so
7512    /// the parent can keep its child rule context and continue at a sync token.
7513    fn fast_child_rule_failure_recovery(
7514        &mut self,
7515        rule_index: usize,
7516        start_index: usize,
7517        sync_symbols: &BTreeSet<i32>,
7518        expected: &ExpectedTokens,
7519    ) -> Option<FastRecognizeOutcome> {
7520        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7521        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7522        let mut next_index = error_index;
7523        loop {
7524            let symbol = self.token_type_at(next_index);
7525            if sync_symbols.contains(&symbol) {
7526                if next_index == error_index {
7527                    return None;
7528                }
7529                break;
7530            }
7531            if symbol == TOKEN_EOF {
7532                break;
7533            }
7534            let after = self.consume_index(next_index, symbol);
7535            if after == next_index {
7536                break;
7537            }
7538            next_index = after;
7539        }
7540        let diagnostics = self
7541            .recognition_arena
7542            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7543        let mut nodes = NodeSeqId::EMPTY;
7544        if self.fast_token_nodes_enabled {
7545            let error = self.arena_token_node(error_index, true);
7546            self.arena_prepend(&mut nodes, error);
7547        }
7548        Some(FastRecognizeOutcome {
7549            index: next_index,
7550            consumed_eof: false,
7551            diagnostics,
7552            deferred_nodes: FastDeferredNodeId::EMPTY,
7553            nodes,
7554        })
7555    }
7556
7557    /// Adapts the optional child-rule recovery result to the fast-recognizer
7558    /// outcome list used by rule-call transitions.
7559    fn fast_child_rule_failure_recovery_outcomes(
7560        &mut self,
7561        request: FastChildRuleFailureRecoveryRequest<'_>,
7562    ) -> Vec<FastRecognizeOutcome> {
7563        let FastChildRuleFailureRecoveryRequest {
7564            atn,
7565            rule_index,
7566            start_index,
7567            follow_state,
7568            stop_state,
7569            expected,
7570        } = request;
7571        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
7572        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
7573            .into_iter()
7574            .collect()
7575    }
7576
7577    fn defer_fast_outcome_node(
7578        &mut self,
7579        outcome: &mut FastRecognizeOutcome,
7580        node: RecognizedNodeId,
7581    ) {
7582        if outcome.deferred_nodes.is_empty() {
7583            self.arena_prepend(&mut outcome.nodes, node);
7584            return;
7585        }
7586        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
7587        let fragment = self.recognition_arena.deferred_fragment(fragment);
7588        outcome.deferred_nodes = self
7589            .recognition_arena
7590            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
7591    }
7592
7593    fn materialize_fast_deferred_nodes(
7594        &mut self,
7595        root: FastDeferredNodeId,
7596        initial_suffix: NodeSeqId,
7597    ) -> NodeSeqId {
7598        if root.is_empty() {
7599            return initial_suffix;
7600        }
7601
7602        enum Frame {
7603            Visit(FastDeferredNodeId),
7604            ContinuePrefix(FastDeferredNodeId),
7605            FinishRule {
7606                rule: FastDeferredRule,
7607                parent_suffix: NodeSeqId,
7608            },
7609        }
7610
7611        let mut result = initial_suffix;
7612        let mut pending = Vec::with_capacity(16);
7613        pending.push(Frame::Visit(root));
7614        let mut fragment_nodes = Vec::new();
7615        while let Some(frame) = pending.pop() {
7616            match frame {
7617                Frame::Visit(deferred) => {
7618                    if deferred.is_empty() {
7619                        continue;
7620                    }
7621
7622                    match self.recognition_arena.deferred_node(deferred) {
7623                        FastDeferredNode::Fragment(sequence) => {
7624                            fragment_nodes.clear();
7625                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
7626                            while let Some(node) = fragment_nodes.pop() {
7627                                self.arena_prepend(&mut result, node);
7628                            }
7629                        }
7630                        FastDeferredNode::Rule(rule) => {
7631                            let rule = self.recognition_arena.deferred_rule(rule);
7632                            let parent_suffix = result;
7633                            result = rule.children;
7634                            pending.push(Frame::FinishRule {
7635                                rule,
7636                                parent_suffix,
7637                            });
7638                            pending.push(Frame::Visit(rule.deferred_children));
7639                        }
7640                        FastDeferredNode::Concat {
7641                            prefix,
7642                            suffix: deferred_suffix,
7643                        } => {
7644                            pending.push(Frame::ContinuePrefix(prefix));
7645                            pending.push(Frame::Visit(deferred_suffix));
7646                        }
7647                    }
7648                }
7649                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
7650                Frame::FinishRule {
7651                    rule,
7652                    parent_suffix,
7653                } => {
7654                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
7655                        rule_index: rule.rule_index,
7656                        invoking_state: rule.invoking_state,
7657                        alt_number: 0,
7658                        start_index: rule.start_index,
7659                        stop_index: rule.stop_index,
7660                        return_values: None,
7661                        children: result,
7662                    });
7663                    result = parent_suffix;
7664                    self.arena_prepend(&mut result, node);
7665                }
7666            }
7667        }
7668        result
7669    }
7670
7671    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) {
7672        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
7673        outcome.nodes = self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
7674    }
7675
7676    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
7677    /// heap work instead of the native call stack.
7678    fn recognize_repetition_fast(
7679        &mut self,
7680        atn: &Atn,
7681        request: &FastRecognizeRequest,
7682        shape: FastRepetitionShape,
7683        scratch: FastRecognizeScratch<'_, '_>,
7684    ) -> Vec<FastRecognizeOutcome> {
7685        let FastRecognizeScratch {
7686            predicate_context,
7687            visiting,
7688            memo,
7689            expected,
7690        } = scratch;
7691        let lookahead = if self.fast_first_set_prefilter {
7692            atn.state(request.state_number).and_then(|state| {
7693                state
7694                    .rule_index()
7695                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
7696                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
7697            })
7698        } else {
7699            None
7700        };
7701        let mut work = Vec::with_capacity(2);
7702        push_fast_repetition_work(
7703            &mut work,
7704            shape,
7705            FastRepetitionPath {
7706                index: request.index,
7707                deferred_nodes: FastDeferredNodeId::EMPTY,
7708                diagnostics: DiagnosticSeqId::EMPTY,
7709                consumed_eof: false,
7710            },
7711            lookahead.as_deref(),
7712            self.token_type_at(request.index),
7713        );
7714        let mut coordinates = FastRepetitionCoordinates::new(request.index);
7715        let mut outcomes = Vec::new();
7716        while let Some(item) = work.pop() {
7717            match item {
7718                FastRepetitionWork::Enter(path) => {
7719                    if !coordinates.insert_entered(path) {
7720                        continue;
7721                    }
7722                    let body_outcomes = self.recognize_state_fast(
7723                        atn,
7724                        FastRecognizeRequest {
7725                            state_number: shape.enter_target,
7726                            stop_state: shape.body_stop_state,
7727                            index: path.index,
7728                            rule_start_index: request.rule_start_index,
7729                            decision_start_index: request.decision_start_index,
7730                            precedence: request.precedence,
7731                            depth: request.depth.saturating_add(1),
7732                            recovery_symbols: Rc::clone(&request.recovery_symbols),
7733                            recovery_state: request.recovery_state,
7734                        },
7735                        FastRecognizeScratch {
7736                            predicate_context,
7737                            visiting: &mut *visiting,
7738                            memo: &mut *memo,
7739                            expected: &mut *expected,
7740                        },
7741                    );
7742                    for body in body_outcomes.into_iter().rev() {
7743                        // ANTLR rejects nullable repetition bodies. Keep the
7744                        // interpreter bounded for malformed or recovered ATNs
7745                        // by mirroring the existing same-coordinate cycle cut.
7746                        if body.index <= path.index {
7747                            continue;
7748                        }
7749                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
7750                        let body_nodes = self
7751                            .recognition_arena
7752                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
7753                        let deferred_nodes = self
7754                            .recognition_arena
7755                            .concat_deferred_nodes(path.deferred_nodes, body_nodes);
7756                        let next_path = FastRepetitionPath {
7757                            index: body.index,
7758                            deferred_nodes,
7759                            diagnostics: self
7760                                .recognition_arena
7761                                .concat_diagnostics(path.diagnostics, body.diagnostics),
7762                            consumed_eof: path.consumed_eof || body.consumed_eof,
7763                        };
7764                        let symbol = self.token_type_at(next_path.index);
7765                        push_fast_repetition_work(
7766                            &mut work,
7767                            shape,
7768                            next_path,
7769                            lookahead.as_deref(),
7770                            symbol,
7771                        );
7772                    }
7773                }
7774                FastRepetitionWork::Exit(path) => {
7775                    if !coordinates.insert_exited(path) {
7776                        continue;
7777                    }
7778                    let suffixes = self.recognize_state_fast(
7779                        atn,
7780                        FastRecognizeRequest {
7781                            state_number: shape.exit_target,
7782                            stop_state: request.stop_state,
7783                            index: path.index,
7784                            rule_start_index: request.rule_start_index,
7785                            decision_start_index: request.decision_start_index,
7786                            precedence: request.precedence,
7787                            depth: request.depth.saturating_add(1),
7788                            recovery_symbols: Rc::clone(&request.recovery_symbols),
7789                            recovery_state: request.recovery_state,
7790                        },
7791                        FastRecognizeScratch {
7792                            predicate_context,
7793                            visiting: &mut *visiting,
7794                            memo: &mut *memo,
7795                            expected: &mut *expected,
7796                        },
7797                    );
7798                    for mut outcome in suffixes {
7799                        outcome.deferred_nodes = self
7800                            .recognition_arena
7801                            .concat_deferred_nodes(path.deferred_nodes, outcome.deferred_nodes);
7802                        outcome.diagnostics = self
7803                            .recognition_arena
7804                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
7805                        outcome.consumed_eof |= path.consumed_eof;
7806                        outcomes.push(outcome);
7807                    }
7808                }
7809            }
7810        }
7811        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
7812        outcomes
7813    }
7814
7815    /// Attempts to reach `stop_state` from `state_number` without committing
7816    /// token consumption to the parser's public stream position.
7817    #[allow(clippy::too_many_lines)]
7818    fn recognize_state_fast(
7819        &mut self,
7820        atn: &Atn,
7821        request: FastRecognizeRequest,
7822        scratch: FastRecognizeScratch<'_, '_>,
7823    ) -> Vec<FastRecognizeOutcome> {
7824        #[cfg(feature = "perf-counters")]
7825        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
7826        let FastRecognizeScratch {
7827            predicate_context,
7828            visiting,
7829            memo,
7830            expected,
7831        } = scratch;
7832        let FastRecognizeRequest {
7833            mut state_number,
7834            stop_state,
7835            mut index,
7836            rule_start_index,
7837            decision_start_index,
7838            precedence,
7839            mut depth,
7840            recovery_symbols,
7841            recovery_state,
7842        } = request;
7843        let max_token_type = atn.max_token_type();
7844        // Walk straight-line epsilon chains in a loop instead of recursing
7845        // into `recognize_state_fast` for each intermediate state. ATN
7846        // serialization places long sequences of `BasicBlock` epsilon
7847        // transitions between decisions: turning that chain into a loop
7848        // collapses many recursive calls (and their memo lookups, vec
7849        // allocations, and visit-set churn) into a single function frame.
7850        // The loop exits as soon as we hit the original state's logic
7851        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
7852        // precedence) so existing fanout, recovery, and memoization still
7853        // apply unchanged.
7854        //
7855        // The inline case also handles single-atom-match states on the
7856        // happy-pass path: when the lone consuming transition matches the
7857        // current lookahead, advance the index and continue without paying
7858        // for a full `recognize_state_fast` recursion. We track tokens we
7859        // consumed inline in `inline_consumed_tokens` so they can be
7860        // prepended onto the eventual outcome list once we hit a state
7861        // whose handling falls outside this fast loop.
7862        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
7863        let mut inline_consumed_eof = false;
7864        loop {
7865            if depth > RECOGNITION_DEPTH_LIMIT {
7866                return Vec::new();
7867            }
7868            if state_number == stop_state {
7869                let mut nodes = NodeSeqId::EMPTY;
7870                if self.fast_token_nodes_enabled {
7871                    for token_index in inline_consumed_tokens.iter().rev() {
7872                        let token = self.arena_token_node(*token_index, false);
7873                        self.arena_prepend(&mut nodes, token);
7874                    }
7875                }
7876                return vec![FastRecognizeOutcome {
7877                    index,
7878                    consumed_eof: inline_consumed_eof,
7879                    diagnostics: DiagnosticSeqId::EMPTY,
7880                    deferred_nodes: FastDeferredNodeId::EMPTY,
7881                    nodes,
7882                }];
7883            }
7884            let Some(state) = atn.state(state_number) else {
7885                return Vec::new();
7886            };
7887            let transitions = state.transitions();
7888            if transitions.len() == 1 && !state.precedence_rule_decision() {
7889                let transition = transitions
7890                    .first()
7891                    .expect("single transition checked above");
7892                let transition_kind = transition.kind();
7893                let target = transition.target();
7894                match transition_kind {
7895                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
7896                        if left_recursive_boundary(atn, state, target).is_none() =>
7897                    {
7898                        #[cfg(feature = "perf-counters")]
7899                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
7900                        state_number = target;
7901                        depth += 1;
7902                        continue;
7903                    }
7904                    ParserTransitionKind::Predicate
7905                        if left_recursive_boundary(atn, state, target).is_none() =>
7906                    {
7907                        #[cfg(feature = "perf-counters")]
7908                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
7909                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
7910                        {
7911                            record_predicate_no_viable(expected, decision_start_index, index);
7912                            return Vec::new();
7913                        }
7914                        state_number = target;
7915                        depth += 1;
7916                        continue;
7917                    }
7918                    ParserTransitionKind::Precedence
7919                        if packed_i32(transition.arg0()) >= precedence
7920                            && left_recursive_boundary(atn, state, target).is_none() =>
7921                    {
7922                        #[cfg(feature = "perf-counters")]
7923                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
7924                        state_number = target;
7925                        depth += 1;
7926                        continue;
7927                    }
7928                    // Single-atom / range / set / wildcard / not-set states
7929                    // are common (~17K of ~125K calls on C#) and almost
7930                    // always succeed in pass 1: no fanout, no recovery, no
7931                    // diagnostics. Inline the token match and continue
7932                    // walking instead of recursing — the recursive path
7933                    // would just allocate a Vec, build one outcome, prepend
7934                    // a Token node, and return. Skip pass 2 (recovery
7935                    // enabled): there the failure branch matters and the
7936                    // existing recursive code records expected symbols.
7937                    ParserTransitionKind::Atom
7938                    | ParserTransitionKind::Range
7939                    | ParserTransitionKind::Set
7940                    | ParserTransitionKind::NotSet
7941                    | ParserTransitionKind::Wildcard
7942                        if !self.fast_recovery_enabled =>
7943                    {
7944                        let symbol = self.token_type_at(index);
7945                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
7946                            #[cfg(feature = "perf-counters")]
7947                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
7948                            if self.fast_token_nodes_enabled {
7949                                inline_consumed_tokens.push(index);
7950                            }
7951                            inline_consumed_eof |= symbol == TOKEN_EOF;
7952                            index = self.consume_index(index, symbol);
7953                            state_number = target;
7954                            depth += 1;
7955                            continue;
7956                        }
7957                        // Fall through to break and let the regular
7958                        // body handle the no-match case (returns empty).
7959                    }
7960                    _ => {}
7961                }
7962            }
7963            break;
7964        }
7965        // If we collected token nodes inline but bail to the recursive
7966        // body (decision state, rule call, etc.), the outcomes returned
7967        // below will need those token nodes prepended.
7968        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
7969        let Some(state) = atn.state(state_number) else {
7970            return Vec::new();
7971        };
7972        let transitions = state.transitions();
7973        let transition_count = transitions.len();
7974        if !self.fast_recovery_enabled
7975            && let Some(shape) = fast_repetition_shape(atn, state)
7976        {
7977            let mut outcomes = self.recognize_repetition_fast(
7978                atn,
7979                &FastRecognizeRequest {
7980                    state_number,
7981                    stop_state,
7982                    index,
7983                    rule_start_index,
7984                    decision_start_index,
7985                    precedence,
7986                    depth,
7987                    recovery_symbols: Rc::clone(&recovery_symbols),
7988                    recovery_state,
7989                },
7990                shape,
7991                FastRecognizeScratch {
7992                    predicate_context,
7993                    visiting: &mut *visiting,
7994                    memo: &mut *memo,
7995                    expected: &mut *expected,
7996                },
7997            );
7998            if inline_pending {
7999                for outcome in &mut outcomes {
8000                    outcome.consumed_eof |= inline_consumed_eof;
8001                    if self.fast_token_nodes_enabled {
8002                        for token_index in inline_consumed_tokens.iter().rev() {
8003                            let token = self.arena_token_node(*token_index, false);
8004                            self.defer_fast_outcome_node(outcome, token);
8005                        }
8006                    }
8007                }
8008            }
8009            return outcomes;
8010        }
8011        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
8012        // fields and the rule/decision boundary indices are pure plumbing —
8013        // they only affect the recovery branch and the no-viable diagnostic
8014        // recording, neither of which fires when recovery is off. Zeroing
8015        // them in the memo key collapses calls that visit the same
8016        // `(state, index)` from different rule-call sites onto one cache
8017        // entry, which is the dominant cost on large grammars (e.g. C#) where
8018        // many rules eventually delegate into the same `expression` /
8019        // `primary_expression` / `type` branches.
8020        let key = if self.fast_recovery_enabled {
8021            FastRecognizeKey {
8022                state_number,
8023                stop_state,
8024                index,
8025                rule_start_index,
8026                decision_start_index,
8027                precedence,
8028                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
8029                recovery_state,
8030            }
8031        } else {
8032            FastRecognizeKey {
8033                state_number,
8034                stop_state,
8035                index,
8036                rule_start_index: 0,
8037                decision_start_index: None,
8038                precedence,
8039                recovery_symbols_id: 0,
8040                recovery_state: None,
8041            }
8042        };
8043        // Once the clean-pass probe has established that coordinates do not
8044        // repeat, stop paying for the full memo table. Recovery always keeps
8045        // memoization because cached failures carry diagnostics, while
8046        // repeat-heavy clean parses promote before reaching sparse mode.
8047        let memo_lookup_enabled = self.fast_recovery_enabled
8048            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
8049        if memo_lookup_enabled {
8050            if let Some(outcomes) = memo.get(&key) {
8051                #[cfg(feature = "perf-counters")]
8052                {
8053                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
8054                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
8055                }
8056                // Materialize a fresh `Vec` from the cached slice; the caller
8057                // mutates per-outcome state (eof flags, prepended nodes) so we
8058                // can't hand them the shared backing.
8059                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
8060                    let inline_eof = inline_consumed_eof;
8061                    let inline_tokens = &inline_consumed_tokens;
8062                    return outcomes
8063                        .iter()
8064                        .copied()
8065                        .map(|mut outcome| {
8066                            if inline_eof {
8067                                outcome.consumed_eof = true;
8068                            }
8069                            if self.fast_token_nodes_enabled {
8070                                for token_index in inline_tokens.iter().rev() {
8071                                    let token = self.arena_token_node(*token_index, false);
8072                                    self.defer_fast_outcome_node(&mut outcome, token);
8073                                }
8074                            }
8075                            outcome
8076                        })
8077                        .collect();
8078                }
8079                return outcomes.to_vec();
8080            }
8081            #[cfg(feature = "perf-counters")]
8082            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
8083        }
8084
8085        // Cycle detection: clean recognition keeps the narrow static cycle
8086        // guard used on hot paths. Recovery needs the broader epsilon-state
8087        // guard because an otherwise non-nullable loop body can recover as an
8088        // empty child at EOF and re-enter the loop at the same token.
8089        let needs_cycle_guard = if self.fast_recovery_enabled {
8090            transitions.iter().any(ParserTransition::is_epsilon)
8091        } else {
8092            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
8093        };
8094        #[cfg(feature = "perf-counters")]
8095        if needs_cycle_guard {
8096            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
8097        } else {
8098            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
8099            match state
8100                .transitions()
8101                .first()
8102                .expect("single-transition path requires one transition")
8103                .data()
8104            {
8105                Transition::Rule { .. } => {
8106                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
8107                }
8108                Transition::Atom { .. }
8109                | Transition::Range { .. }
8110                | Transition::Set { .. }
8111                | Transition::NotSet { .. }
8112                | Transition::Wildcard { .. } => {
8113                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
8114                }
8115                _ => {
8116                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
8117                }
8118            }
8119        }
8120        let has_inserted_cycle_guard = if needs_cycle_guard {
8121            if !visiting.insert(key.clone()) {
8122                #[cfg(feature = "perf-counters")]
8123                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
8124                return Vec::new();
8125            }
8126            true
8127        } else {
8128            false
8129        };
8130        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
8131            Some(index)
8132        } else {
8133            decision_start_index
8134        };
8135        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
8136            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
8137        } else {
8138            (Rc::clone(&recovery_symbols), recovery_state)
8139        };
8140
8141        // Lookahead-based pruning. At a multi-alternative state we cache the
8142        // look-1 set of every outgoing transition; on visit we keep only the
8143        // transitions whose look-1 can accept the current lookahead (or that
8144        // can be reached without consuming and so could legitimately match a
8145        // shorter input). This is the main speedup vs. blind speculative
8146        // recursion: it lets each visit fan out only to the alternatives that
8147        // could possibly contribute a clean parse, mirroring the SLL phase of
8148        // ANTLR's adaptive prediction.
8149        //
8150        // Pruning is skipped at:
8151        //   * rule-start states (a child rule call may need every internal
8152        //     transition to surface single-token recovery diagnostics that
8153        //     ANTLR's reference parser emits at the rule's first consuming
8154        //     transition; the FIRST-set retry path turns the prefilter off
8155        //     entirely so let's keep this lightweight too),
8156        //   * left-recursive precedence loops (the precedence transition's
8157        //     gating is dynamic),
8158        //   * states with too few alternatives to benefit.
8159        let lookahead_filter = if transition_count > 1
8160            && self.fast_first_set_prefilter
8161            && !state.precedence_rule_decision()
8162            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
8163        {
8164            state
8165                .rule_index()
8166                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8167                .map(|rule_stop| {
8168                    let symbol = self.token_type_at(index);
8169                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
8170                    (symbol, entry)
8171                })
8172        } else {
8173            None
8174        };
8175        // LL(1) fast path: when the FIRST sets for the decision are disjoint
8176        // and none is nullable, the lookahead deterministically selects one
8177        // alternative. The recursive recognizer can then commit to that single
8178        // alt without iterating every transition through `should_skip_via_lookahead`
8179        // — saving (transition_count - 1) filter probes per visit.
8180        //
8181        // Result is cached per `(state, lookahead_token)` on the parser
8182        // instance, so subsequent visits skip the FIRST-set scan entirely.
8183        let ll1_only_alt: Option<usize> = if transition_count > 1
8184            && let Some((symbol, entry)) = lookahead_filter.as_ref()
8185        {
8186            let key = (state.state_number(), *symbol);
8187            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
8188                cached
8189            } else {
8190                let result = ll1_unique_alt(entry, *symbol);
8191                self.ll1_decision_cache.insert(key, result);
8192                result
8193            }
8194        } else {
8195            None
8196        };
8197        let lookahead_filter = lookahead_filter.as_ref();
8198        // Pre-size only when we expect at least one outcome to land — most
8199        // single-transition fall-throughs (the loop above didn't catch
8200        // because they're atom/rule/predicate) push at most one entry, so
8201        // reserving one slot avoids a reallocation while keeping the
8202        // unused-slot waste at one element.
8203        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
8204        for (transition_index, transition) in transitions.iter().enumerate() {
8205            if let Some(alt) = ll1_only_alt {
8206                // LL(1) determinism: skip every alt except the chosen one.
8207                if alt != transition_index {
8208                    continue;
8209                }
8210            }
8211            let transition_kind = transition.kind();
8212            if ll1_only_alt.is_none()
8213                && should_skip_via_lookahead(
8214                    transition_kind,
8215                    transition_index,
8216                    lookahead_filter,
8217                    index,
8218                    self.fast_recovery_enabled,
8219                    expected,
8220                )
8221            {
8222                continue;
8223            }
8224            let target = transition.target();
8225            match transition_kind {
8226                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
8227                    #[cfg(feature = "perf-counters")]
8228                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8229                    let boundary = left_recursive_boundary(atn, state, target);
8230                    outcomes.extend(
8231                        self.recognize_state_fast(
8232                            atn,
8233                            FastRecognizeRequest {
8234                                state_number: target,
8235                                stop_state,
8236                                index,
8237                                rule_start_index,
8238                                decision_start_index: next_decision_start_index,
8239                                precedence,
8240                                depth: depth + 1,
8241                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8242                                recovery_state: epsilon_recovery_state,
8243                            },
8244                            FastRecognizeScratch {
8245                                predicate_context,
8246                                visiting,
8247                                memo,
8248                                expected,
8249                            },
8250                        )
8251                        .into_iter()
8252                        .map(|mut outcome| {
8253                            if let Some(rule_index) = boundary {
8254                                let boundary = self.arena_boundary_node(rule_index);
8255                                self.defer_fast_outcome_node(&mut outcome, boundary);
8256                            }
8257                            outcome
8258                        }),
8259                    );
8260                }
8261                ParserTransitionKind::Predicate => {
8262                    #[cfg(feature = "perf-counters")]
8263                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8264                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
8265                        let boundary = left_recursive_boundary(atn, state, target);
8266                        outcomes.extend(
8267                            self.recognize_state_fast(
8268                                atn,
8269                                FastRecognizeRequest {
8270                                    state_number: target,
8271                                    stop_state,
8272                                    index,
8273                                    rule_start_index,
8274                                    decision_start_index: next_decision_start_index,
8275                                    precedence,
8276                                    depth: depth + 1,
8277                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8278                                    recovery_state: epsilon_recovery_state,
8279                                },
8280                                FastRecognizeScratch {
8281                                    predicate_context,
8282                                    visiting,
8283                                    memo,
8284                                    expected,
8285                                },
8286                            )
8287                            .into_iter()
8288                            .map(|mut outcome| {
8289                                if let Some(rule_index) = boundary {
8290                                    let boundary = self.arena_boundary_node(rule_index);
8291                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8292                                }
8293                                outcome
8294                            }),
8295                        );
8296                    } else {
8297                        record_predicate_no_viable(expected, next_decision_start_index, index);
8298                    }
8299                }
8300                ParserTransitionKind::Precedence => {
8301                    let transition_precedence = packed_i32(transition.arg0());
8302                    if transition_precedence >= precedence {
8303                        let boundary = left_recursive_boundary(atn, state, target);
8304                        outcomes.extend(
8305                            self.recognize_state_fast(
8306                                atn,
8307                                FastRecognizeRequest {
8308                                    state_number: target,
8309                                    stop_state,
8310                                    index,
8311                                    rule_start_index,
8312                                    decision_start_index: next_decision_start_index,
8313                                    precedence,
8314                                    depth: depth + 1,
8315                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8316                                    recovery_state: epsilon_recovery_state,
8317                                },
8318                                FastRecognizeScratch {
8319                                    predicate_context,
8320                                    visiting,
8321                                    memo,
8322                                    expected,
8323                                },
8324                            )
8325                            .into_iter()
8326                            .map(|mut outcome| {
8327                                if let Some(rule_index) = boundary {
8328                                    let boundary = self.arena_boundary_node(rule_index);
8329                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8330                                }
8331                                outcome
8332                            }),
8333                        );
8334                    }
8335                }
8336                ParserTransitionKind::Rule => {
8337                    let rule_index = transition.arg0() as usize;
8338                    let follow_state = transition.arg1() as usize;
8339                    let rule_precedence = packed_i32(transition.arg2());
8340                    #[cfg(feature = "perf-counters")]
8341                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
8342                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
8343                        continue;
8344                    };
8345                    // Lookahead-based pruning. The recognizer would otherwise
8346                    // explore every speculative rule call, producing exponential
8347                    // work on grammars with many epsilon-reachable rules. When
8348                    // the rule is non-nullable and its FIRST set excludes the
8349                    // current lookahead, recursion can't find a clean path
8350                    // *through this rule*. Skipping is only safe if some sibling
8351                    // transition can still consume the lookahead — otherwise the
8352                    // rule call is the sole continuation and must run so the
8353                    // single-token insertion / deletion recovery inside the
8354                    // called rule can fire (mirroring ANTLR's reference behavior
8355                    // of conjuring a missing token at child-rule entry).
8356                    let symbol = self.token_type_at(index);
8357                    if self.fast_first_set_prefilter {
8358                        // Probe the shared cross-parse cache first; build
8359                        // the entry on miss and intern it there. The
8360                        // computation is purely a function of the ATN, so
8361                        // the cached entry is reused across parses (and
8362                        // freshly-instantiated parser values that share
8363                        // the same `&'static Atn`).
8364                        //
8365                        // `rule_first_set` returns the computed entry
8366                        // directly — it intentionally skips inserting into
8367                        // the cache when the FIRST-set walk hit a cycle, so
8368                        // we cannot assume the entry is in the cache after
8369                        // computing it.
8370                        let first = self.cached_rule_first_set(atn, target, child_stop);
8371                        if should_skip_rule_via_first_set(
8372                            &first,
8373                            symbol,
8374                            self.fast_recovery_enabled,
8375                            index,
8376                            expected,
8377                        ) {
8378                            continue;
8379                        }
8380                    }
8381                    let expected_before_child =
8382                        self.fast_recovery_enabled.then(|| expected.clone());
8383                    let mut children = self.recognize_state_fast(
8384                        atn,
8385                        FastRecognizeRequest {
8386                            state_number: target,
8387                            stop_state: child_stop,
8388                            index,
8389                            rule_start_index: index,
8390                            decision_start_index: None,
8391                            precedence: rule_precedence,
8392                            depth: depth + 1,
8393                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8394                            recovery_state: epsilon_recovery_state,
8395                        },
8396                        FastRecognizeScratch {
8397                            predicate_context,
8398                            visiting,
8399                            memo,
8400                            expected,
8401                        },
8402                    );
8403                    if children.is_empty() && self.fast_recovery_enabled {
8404                        children = self.fast_child_rule_failure_recovery_outcomes(
8405                            FastChildRuleFailureRecoveryRequest {
8406                                atn,
8407                                rule_index,
8408                                start_index: index,
8409                                follow_state,
8410                                stop_state,
8411                                expected,
8412                            },
8413                        );
8414                    }
8415                    if let Some(expected_before_child) = expected_before_child {
8416                        if children
8417                            .iter()
8418                            .any(|child| child.diagnostics.is_empty() && child.index > index)
8419                        {
8420                            *expected = expected_before_child;
8421                        }
8422                    }
8423                    for child in children {
8424                        let child_index = child.index;
8425                        let child_consumed_eof = child.consumed_eof;
8426                        let child_diagnostics = child.diagnostics;
8427                        let empty_recovery = self.empty_recovery_symbols();
8428                        let follow_outcomes = self.recognize_state_fast(
8429                            atn,
8430                            FastRecognizeRequest {
8431                                state_number: follow_state,
8432                                stop_state,
8433                                index: child_index,
8434                                rule_start_index,
8435                                decision_start_index: next_decision_start_index,
8436                                precedence,
8437                                depth: depth + 1,
8438                                recovery_symbols: empty_recovery,
8439                                recovery_state: None,
8440                            },
8441                            FastRecognizeScratch {
8442                                predicate_context,
8443                                visiting,
8444                                memo,
8445                                expected,
8446                            },
8447                        );
8448                        if follow_outcomes.is_empty() {
8449                            continue;
8450                        }
8451                        let child_stop_index =
8452                            self.rule_stop_token_index(child_index, child_consumed_eof);
8453                        let child_node = self.build_parse_trees.then(|| {
8454                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
8455                                rule_index: u32::try_from(rule_index)
8456                                    .expect("rule index fits in u32"),
8457                                invoking_state: i32::try_from(invoking_state_number(state_number))
8458                                    .expect("invoking state fits in i32"),
8459                                start_index: u32::try_from(index)
8460                                    .expect("rule start index fits in u32"),
8461                                stop_index: child_stop_index.map(|stop_index| {
8462                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
8463                                }),
8464                                deferred_children: child.deferred_nodes,
8465                                children: child.nodes,
8466                            })
8467                        });
8468                        let child_diags_empty = child_diagnostics.is_empty();
8469                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
8470                            outcome.consumed_eof |= child_consumed_eof;
8471                            // Skip the prepend dance when there's nothing to
8472                            // merge from the child — common case in pass 1.
8473                            if !child_diags_empty {
8474                                outcome.diagnostics = self
8475                                    .recognition_arena
8476                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
8477                            }
8478                            if let Some(child_node) = child_node {
8479                                outcome.deferred_nodes = self
8480                                    .recognition_arena
8481                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
8482                            }
8483                            outcome
8484                        }));
8485                    }
8486                }
8487                ParserTransitionKind::Atom
8488                | ParserTransitionKind::Range
8489                | ParserTransitionKind::Set
8490                | ParserTransitionKind::NotSet
8491                | ParserTransitionKind::Wildcard => {
8492                    #[cfg(feature = "perf-counters")]
8493                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8494                    let symbol = self.token_type_at(index);
8495                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8496                        let next_index = self.consume_index(index, symbol);
8497                        let empty_recovery = self.empty_recovery_symbols();
8498                        outcomes.extend(
8499                            self.recognize_state_fast(
8500                                atn,
8501                                FastRecognizeRequest {
8502                                    state_number: target,
8503                                    stop_state,
8504                                    index: next_index,
8505                                    rule_start_index,
8506                                    decision_start_index: next_decision_start_index,
8507                                    precedence,
8508                                    depth: depth + 1,
8509                                    recovery_symbols: empty_recovery,
8510                                    recovery_state: None,
8511                                },
8512                                FastRecognizeScratch {
8513                                    predicate_context,
8514                                    visiting,
8515                                    memo,
8516                                    expected,
8517                                },
8518                            )
8519                            .into_iter()
8520                            .map(|mut outcome| {
8521                                outcome.consumed_eof |= symbol == TOKEN_EOF;
8522                                if self.fast_token_nodes_enabled {
8523                                    let token = self.arena_token_node(index, false);
8524                                    self.defer_fast_outcome_node(&mut outcome, token);
8525                                }
8526                                outcome
8527                            }),
8528                        );
8529                    } else {
8530                        if !self.fast_recovery_enabled {
8531                            // In pass 1 there is no recovery to attempt; the
8532                            // recovery branch below would never run, and the
8533                            // `expected_symbols` computation is just there
8534                            // to gate that branch. Skipping it eliminates
8535                            // ~1× `state_expected_symbols` lookup per failed
8536                            // atom transition (≈82K on mono-statement.cs)
8537                            // for zero observable behavior change.
8538                            continue;
8539                        }
8540                        let expected_symbols = fast_recovery_expected_symbols(
8541                            self,
8542                            atn,
8543                            state.state_number(),
8544                            &recovery_symbols,
8545                        );
8546                        if expected_symbols.contains(&symbol) {
8547                            continue;
8548                        }
8549                        {
8550                            expected.record_transition(index, transition, max_token_type);
8551                            record_no_viable_if_ambiguous(
8552                                expected,
8553                                next_decision_start_index,
8554                                index,
8555                            );
8556                            outcomes.extend(self.fast_single_token_deletion_recovery(
8557                                FastRecoveryRequest {
8558                                    atn,
8559                                    transition,
8560                                    expected_symbols: Rc::clone(&expected_symbols),
8561                                    target,
8562                                    request: FastRecognizeRequest {
8563                                        state_number,
8564                                        stop_state,
8565                                        index,
8566                                        rule_start_index,
8567                                        decision_start_index,
8568                                        precedence,
8569                                        depth,
8570                                        recovery_symbols: Rc::clone(&recovery_symbols),
8571                                        recovery_state,
8572                                    },
8573                                    visiting,
8574                                    memo,
8575                                    expected,
8576                                },
8577                                predicate_context,
8578                            ));
8579                            if !state_is_left_recursive_rule(atn, state) {
8580                                outcomes.extend(self.fast_single_token_insertion_recovery(
8581                                    FastRecoveryRequest {
8582                                        atn,
8583                                        transition,
8584                                        expected_symbols: Rc::clone(&expected_symbols),
8585                                        target,
8586                                        request: FastRecognizeRequest {
8587                                            state_number,
8588                                            stop_state,
8589                                            index,
8590                                            rule_start_index,
8591                                            decision_start_index,
8592                                            precedence,
8593                                            depth,
8594                                            recovery_symbols: Rc::clone(&recovery_symbols),
8595                                            recovery_state,
8596                                        },
8597                                        visiting,
8598                                        memo,
8599                                        expected,
8600                                    },
8601                                    predicate_context,
8602                                ));
8603                            }
8604                            outcomes.extend(self.fast_current_token_deletion_recovery(
8605                                FastCurrentTokenDeletionRequest {
8606                                    atn,
8607                                    expected_symbols,
8608                                    request: FastRecognizeRequest {
8609                                        state_number,
8610                                        stop_state,
8611                                        index,
8612                                        rule_start_index,
8613                                        decision_start_index,
8614                                        precedence,
8615                                        depth,
8616                                        recovery_symbols: Rc::clone(&recovery_symbols),
8617                                        recovery_state,
8618                                    },
8619                                    visiting,
8620                                    memo,
8621                                    expected,
8622                                },
8623                                predicate_context,
8624                            ));
8625                        }
8626                    }
8627                }
8628            }
8629        }
8630
8631        if has_inserted_cycle_guard {
8632            visiting.remove(&key);
8633        }
8634        if matches!(
8635            self.prediction_mode,
8636            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
8637        ) && self.fast_recovery_enabled
8638        {
8639            // Without recovery enabled every outcome already has empty
8640            // diagnostics, so the discard pass is a no-op — skipping it
8641            // saves an iter+retain on each of the ~1M visits.
8642            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
8643        }
8644        if self.fast_recovery_enabled {
8645            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
8646        } else {
8647            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8648        }
8649        // Skip memoization for single-transition states whose outcome is
8650        // unambiguous: they only get re-entered if the caller revisits the
8651        // exact same call site, which is rare since the loop above already
8652        // collapsed straight-line epsilon walks. Multi-alternative states
8653        // are where backtracking actually revisits the same coordinate, so
8654        // we still memoize there. With recovery on we keep the existing
8655        // memoization unconditionally because the recovery branch may
8656        // record diagnostics that the cache must surface to repeated
8657        // failed visits.
8658        let should_memoize = self.fast_recovery_enabled
8659            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
8660        // Apply inline pending state to each outcome before returning.
8661        // Tokens consumed inline by the loop-collapse don't appear in the
8662        // recursive recognizer's output, so we need to prepend them here.
8663        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
8664            if inline_consumed_eof {
8665                outcome.consumed_eof = true;
8666            }
8667            if !inline_consumed_tokens.is_empty() {
8668                for token_index in inline_consumed_tokens.iter().rev() {
8669                    let token = self.arena_token_node(*token_index, false);
8670                    self.defer_fast_outcome_node(&mut outcome, token);
8671                }
8672            }
8673            outcome
8674        };
8675        if should_memoize {
8676            #[cfg(feature = "perf-counters")]
8677            {
8678                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
8679                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
8680                match outcomes.len() {
8681                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8682                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8683                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8684                }
8685            }
8686            // The memo is keyed by the loop-exit `(state_number, index)` so
8687            // the inline-consumed tokens belong to *this* call's output, not
8688            // the cached result. Memoize the bare outcomes (without the
8689            // inline-pending data), then prepend the inline data on return.
8690            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
8691            memo.insert(key, Rc::clone(&stored));
8692            if inline_pending {
8693                return stored
8694                    .iter()
8695                    .copied()
8696                    .map(&mut apply_inline_pending)
8697                    .collect();
8698            }
8699            return stored.to_vec();
8700        }
8701        #[cfg(feature = "perf-counters")]
8702        match outcomes.len() {
8703            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8704            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8705            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8706        }
8707        if inline_pending {
8708            return outcomes.into_iter().map(apply_inline_pending).collect();
8709        }
8710        outcomes
8711    }
8712
8713    /// Explores single-token deletion recovery while preserving the matched
8714    /// token and skipped error token in the selected parse tree path.
8715    fn single_token_deletion_recovery(
8716        &mut self,
8717        recovery: RecoveryRequest<'_, '_>,
8718    ) -> Vec<RecognizeOutcome> {
8719        let RecoveryRequest {
8720            atn,
8721            transition,
8722            expected_symbols,
8723            target,
8724            request,
8725            visiting,
8726            memo,
8727            expected,
8728        } = recovery;
8729        let RecognizeRequest {
8730            stop_state,
8731            index,
8732            rule_start_index,
8733            decision_start_index,
8734            init_action_rules,
8735            predicates,
8736            semantics,
8737            rule_args,
8738            member_actions,
8739            return_actions,
8740            local_int_arg,
8741            member_values,
8742            return_values,
8743            rule_alt_number,
8744            track_alt_numbers,
8745            consumed_eof,
8746            precedence,
8747            depth,
8748            ..
8749        } = request;
8750        let Some((diagnostic, next_index, next_symbol)) =
8751            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8752        else {
8753            return Vec::new();
8754        };
8755        let after_next = self.consume_index(next_index, next_symbol);
8756        self.recognize_state(
8757            atn,
8758            RecognizeRequest {
8759                state_number: target,
8760                stop_state,
8761                index: after_next,
8762                rule_start_index,
8763                decision_start_index,
8764                init_action_rules,
8765                predicates,
8766                semantics,
8767                rule_args,
8768                member_actions,
8769                return_actions,
8770                local_int_arg,
8771                member_values,
8772                return_values,
8773                rule_alt_number,
8774                track_alt_numbers,
8775                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
8776                precedence,
8777                depth: depth + 1,
8778                recovery_symbols: BTreeSet::new(),
8779                recovery_state: None,
8780            },
8781            visiting,
8782            memo,
8783            expected,
8784        )
8785        .into_iter()
8786        .map(|mut outcome| {
8787            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
8788            outcome.diagnostics = self
8789                .recognition_arena
8790                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8791            let token = self.arena_token_node(next_index, false);
8792            self.arena_prepend(&mut outcome.nodes, token);
8793            let error = self.arena_token_node(index, true);
8794            self.arena_prepend(&mut outcome.nodes, error);
8795            outcome
8796        })
8797        .collect()
8798    }
8799
8800    /// Retries the current recognition state after deleting one unexpected
8801    /// token, preserving the deleted token as an error node in the parse tree.
8802    fn current_token_deletion_recovery(
8803        &mut self,
8804        recovery: CurrentTokenDeletionRequest<'_, '_>,
8805    ) -> Vec<RecognizeOutcome> {
8806        let CurrentTokenDeletionRequest {
8807            atn,
8808            expected_symbols,
8809            mut request,
8810            visiting,
8811            memo,
8812            expected,
8813        } = recovery;
8814        let error_index = request.index;
8815        if error_index == request.rule_start_index {
8816            return Vec::new();
8817        }
8818        let Some((diagnostic, next_index, skipped)) =
8819            self.current_token_deletion(error_index, &expected_symbols)
8820        else {
8821            return Vec::new();
8822        };
8823        request.state_number = request.recovery_state.unwrap_or(request.state_number);
8824        request.index = next_index;
8825        request.depth += 1;
8826        request.recovery_state = None;
8827        self.recognize_state(atn, request, visiting, memo, expected)
8828            .into_iter()
8829            .map(|mut outcome| {
8830                outcome.diagnostics = self
8831                    .recognition_arena
8832                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8833                for index in skipped.iter().rev() {
8834                    let error = self.arena_token_node(*index, true);
8835                    self.arena_prepend(&mut outcome.nodes, error);
8836                }
8837                outcome
8838            })
8839            .collect()
8840    }
8841
8842    /// Falls back after deletion/insertion repairs cannot continue from a
8843    /// failed consuming transition.
8844    fn consuming_failure_fallback(
8845        &mut self,
8846        fallback: ConsumingFailureFallback<'_>,
8847        visiting: &mut BTreeSet<RecognizeKey>,
8848        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
8849        expected: &mut ExpectedTokens,
8850    ) -> Vec<RecognizeOutcome> {
8851        if fallback.expected_symbols.is_empty() {
8852            return Vec::new();
8853        }
8854        if fallback.symbol == TOKEN_EOF {
8855            return self.eof_consuming_failure_fallback(fallback, expected);
8856        }
8857        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
8858    }
8859
8860    /// Keeps unexpected non-EOF input visible as an error node when no repair
8861    /// path can otherwise reach the transition target.
8862    fn non_eof_consuming_failure_fallback(
8863        &mut self,
8864        fallback: ConsumingFailureFallback<'_>,
8865        visiting: &mut BTreeSet<RecognizeKey>,
8866        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
8867        expected: &mut ExpectedTokens,
8868    ) -> Vec<RecognizeOutcome> {
8869        let ConsumingFailureFallback {
8870            atn,
8871            target,
8872            request,
8873            symbol,
8874            expected_symbols,
8875            decision_start_index,
8876            decision,
8877        } = fallback;
8878        let error_index = request.index;
8879        let diagnostic =
8880            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
8881        let next_index = self.consume_index(error_index, symbol);
8882        self.recognize_state(
8883            atn,
8884            RecognizeRequest {
8885                state_number: target,
8886                stop_state: request.stop_state,
8887                index: next_index,
8888                rule_start_index: request.rule_start_index,
8889                decision_start_index,
8890                init_action_rules: request.init_action_rules,
8891                predicates: request.predicates,
8892                semantics: request.semantics,
8893                rule_args: request.rule_args,
8894                member_actions: request.member_actions,
8895                return_actions: request.return_actions,
8896                local_int_arg: request.local_int_arg,
8897                member_values: request.member_values,
8898                return_values: request.return_values,
8899                rule_alt_number: request.rule_alt_number,
8900                track_alt_numbers: request.track_alt_numbers,
8901                consumed_eof: request.consumed_eof,
8902                precedence: request.precedence,
8903                depth: request.depth + 1,
8904                recovery_symbols: BTreeSet::new(),
8905                recovery_state: None,
8906            },
8907            visiting,
8908            memo,
8909            expected,
8910        )
8911        .into_iter()
8912        .map(|mut outcome| {
8913            prepend_decision(&mut outcome, decision);
8914            outcome.diagnostics = self
8915                .recognition_arena
8916                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8917            let error = self.arena_token_node(error_index, true);
8918            self.arena_prepend(&mut outcome.nodes, error);
8919            outcome
8920        })
8921        .collect()
8922    }
8923
8924    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
8925    /// behavior of unwinding instead of inserting caller tokens at EOF.
8926    fn eof_consuming_failure_fallback(
8927        &mut self,
8928        fallback: ConsumingFailureFallback<'_>,
8929        expected: &ExpectedTokens,
8930    ) -> Vec<RecognizeOutcome> {
8931        let request = fallback.request;
8932        if request.index == request.rule_start_index {
8933            return Vec::new();
8934        }
8935        let diagnostic =
8936            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
8937        let diagnostics = self
8938            .recognition_arena
8939            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8940        vec![RecognizeOutcome {
8941            index: request.index,
8942            consumed_eof: request.consumed_eof,
8943            alt_number: request.rule_alt_number,
8944            member_values: request.member_values,
8945            return_values: request.return_values,
8946            diagnostics,
8947            decisions: Vec::new(),
8948            actions: Vec::new(),
8949            nodes: NodeSeqId::EMPTY,
8950        }]
8951    }
8952
8953    /// Explores single-token insertion recovery while adding a conjured
8954    /// missing-token error node to the selected parse tree path.
8955    fn single_token_insertion_recovery(
8956        &mut self,
8957        recovery: RecoveryRequest<'_, '_>,
8958    ) -> Vec<RecognizeOutcome> {
8959        let RecoveryRequest {
8960            atn,
8961            transition,
8962            expected_symbols,
8963            target,
8964            request,
8965            visiting,
8966            memo,
8967            expected,
8968        } = recovery;
8969        let RecognizeRequest {
8970            stop_state,
8971            index,
8972            rule_start_index,
8973            decision_start_index,
8974            init_action_rules,
8975            predicates,
8976            semantics,
8977            rule_args,
8978            member_actions,
8979            return_actions,
8980            local_int_arg,
8981            member_values,
8982            return_values,
8983            rule_alt_number,
8984            track_alt_numbers,
8985            consumed_eof,
8986            precedence,
8987            depth,
8988            ..
8989        } = request;
8990        let follow_symbols = state_expected_symbols(atn, transition.target());
8991        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
8992            transition,
8993            index,
8994            atn.max_token_type(),
8995            &expected_symbols,
8996            &follow_symbols,
8997        ) else {
8998            return Vec::new();
8999        };
9000        self.recognize_state(
9001            atn,
9002            RecognizeRequest {
9003                state_number: target,
9004                stop_state,
9005                index,
9006                rule_start_index,
9007                decision_start_index,
9008                init_action_rules,
9009                predicates,
9010                semantics,
9011                rule_args,
9012                member_actions,
9013                return_actions,
9014                local_int_arg,
9015                member_values,
9016                return_values,
9017                rule_alt_number,
9018                track_alt_numbers,
9019                consumed_eof,
9020                precedence,
9021                depth: depth + 1,
9022                recovery_symbols: BTreeSet::new(),
9023                recovery_state: None,
9024            },
9025            visiting,
9026            memo,
9027            expected,
9028        )
9029        .into_iter()
9030        .map(|mut outcome| {
9031            outcome.diagnostics = self
9032                .recognition_arena
9033                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9034            let missing = self.arena_missing_token_node(token_type, index, text.clone());
9035            self.arena_prepend(&mut outcome.nodes, missing);
9036            outcome
9037        })
9038        .collect()
9039    }
9040
9041    /// Attempts to reach `stop_state` and carries semantic actions for the
9042    /// selected parser path.
9043    #[allow(clippy::too_many_lines)]
9044    fn recognize_state(
9045        &mut self,
9046        atn: &Atn,
9047        request: RecognizeRequest<'_>,
9048        visiting: &mut BTreeSet<RecognizeKey>,
9049        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9050        expected: &mut ExpectedTokens,
9051    ) -> Vec<RecognizeOutcome> {
9052        let request_template = request.clone();
9053        let RecognizeRequest {
9054            state_number,
9055            stop_state,
9056            index,
9057            rule_start_index,
9058            decision_start_index,
9059            init_action_rules,
9060            predicates,
9061            semantics,
9062            rule_args,
9063            member_actions,
9064            return_actions,
9065            local_int_arg,
9066            member_values,
9067            return_values,
9068            rule_alt_number,
9069            track_alt_numbers,
9070            consumed_eof,
9071            precedence,
9072            depth,
9073            recovery_symbols,
9074            recovery_state,
9075        } = request;
9076        if depth > RECOGNITION_DEPTH_LIMIT {
9077            return Vec::new();
9078        }
9079        if state_number == stop_state {
9080            return stop_outcome(
9081                index,
9082                consumed_eof,
9083                rule_alt_number,
9084                member_values,
9085                return_values,
9086            );
9087        }
9088        let key = RecognizeKey {
9089            state_number,
9090            stop_state,
9091            index,
9092            rule_start_index,
9093            decision_start_index,
9094            local_int_arg,
9095            member_values: member_values.clone(),
9096            return_values: return_values.clone(),
9097            rule_alt_number,
9098            track_alt_numbers,
9099            consumed_eof,
9100            precedence,
9101            recovery_symbols: recovery_symbols.clone(),
9102            recovery_state,
9103        };
9104        if let Some(outcomes) = memo.get(&key) {
9105            return outcomes.clone();
9106        }
9107
9108        let visit_key = key.clone();
9109        if !visiting.insert(visit_key.clone()) {
9110            return Vec::new();
9111        }
9112
9113        let Some(state) = atn.state(state_number) else {
9114            visiting.remove(&visit_key);
9115            return Vec::new();
9116        };
9117        let transitions = state.transitions();
9118        let transition_count = transitions.len();
9119        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9120            Some(index)
9121        } else {
9122            decision_start_index
9123        };
9124        let (epsilon_recovery_symbols, epsilon_recovery_state) =
9125            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
9126        let mut outcomes = Vec::new();
9127        for (transition_index, transition) in transitions.iter().enumerate() {
9128            let decision =
9129                transition_decision(atn, state, transition_count, transition_index, predicates);
9130            let next_alt_number = next_alt_number(
9131                state,
9132                transition_count,
9133                transition_index,
9134                rule_alt_number,
9135                track_alt_numbers,
9136            );
9137            let transition_data = transition.data();
9138            match &transition_data {
9139                Transition::Epsilon { target } | Transition::Action { target, .. } => {
9140                    let action_rule_index = match &transition_data {
9141                        Transition::Action { rule_index, .. } => Some(*rule_index),
9142                        _ => None,
9143                    };
9144                    outcomes.extend(self.recognize_epsilon_or_action_step(
9145                        atn,
9146                        &request_template,
9147                        EpsilonActionStep {
9148                            source_state: state_number,
9149                            target: *target,
9150                            action_rule_index,
9151                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
9152                            decision,
9153                            decision_start_index: next_decision_start_index,
9154                            alt_number: next_alt_number,
9155                            recovery_symbols: epsilon_recovery_symbols.clone(),
9156                            recovery_state: epsilon_recovery_state,
9157                        },
9158                        RecognizeScratch {
9159                            visiting,
9160                            memo,
9161                            expected,
9162                        },
9163                    ));
9164                }
9165                Transition::Predicate {
9166                    target,
9167                    rule_index,
9168                    pred_index,
9169                    ..
9170                } => {
9171                    let predicate = PredicateEval {
9172                        index,
9173                        rule_index: *rule_index,
9174                        pred_index: *pred_index,
9175                        predicates,
9176                        semantics,
9177                        context: None,
9178                        local_int_arg,
9179                        member_values: &member_values,
9180                    };
9181                    if self.parser_predicate_matches(predicate) {
9182                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
9183                        outcomes.extend(
9184                            self.recognize_state(
9185                                atn,
9186                                RecognizeRequest {
9187                                    state_number: *target,
9188                                    stop_state,
9189                                    index,
9190                                    rule_start_index,
9191                                    decision_start_index: next_decision_start_index,
9192                                    init_action_rules,
9193                                    predicates,
9194                                    semantics,
9195                                    rule_args,
9196                                    member_actions,
9197                                    return_actions,
9198                                    local_int_arg,
9199                                    member_values: member_values.clone(),
9200                                    return_values: return_values.clone(),
9201                                    rule_alt_number: next_alt_number,
9202                                    track_alt_numbers,
9203                                    consumed_eof,
9204                                    precedence,
9205                                    depth: depth + 1,
9206                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9207                                    recovery_state: epsilon_recovery_state,
9208                                },
9209                                visiting,
9210                                memo,
9211                                expected,
9212                            )
9213                            .into_iter()
9214                            .map(|mut outcome| {
9215                                prepend_decision(&mut outcome, decision);
9216                                if let Some(rule_index) = left_recursive_boundary {
9217                                    let boundary = self.arena_boundary_node(rule_index);
9218                                    self.arena_prepend(&mut outcome.nodes, boundary);
9219                                }
9220                                outcome
9221                            }),
9222                        );
9223                    } else if let Some(message) = semantics
9224                        .and_then(|semantics| {
9225                            self.parser_semantic_ir_predicate_failure_message(
9226                                *rule_index,
9227                                *pred_index,
9228                                semantics,
9229                            )
9230                        })
9231                        .or_else(|| {
9232                            self.parser_predicate_failure_message(
9233                                *rule_index,
9234                                *pred_index,
9235                                predicates,
9236                            )
9237                        })
9238                    {
9239                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
9240                            rule_index: *rule_index,
9241                            index,
9242                            message,
9243                            member_values: member_values.clone(),
9244                            return_values: return_values.clone(),
9245                            rule_alt_number,
9246                        }));
9247                    } else {
9248                        record_predicate_no_viable(expected, next_decision_start_index, index);
9249                    }
9250                }
9251                Transition::Precedence {
9252                    target,
9253                    precedence: transition_precedence,
9254                } => {
9255                    if *transition_precedence >= precedence {
9256                        outcomes.extend(
9257                            self.recognize_state(
9258                                atn,
9259                                RecognizeRequest {
9260                                    state_number: *target,
9261                                    stop_state,
9262                                    index,
9263                                    rule_start_index,
9264                                    decision_start_index: next_decision_start_index,
9265                                    init_action_rules,
9266                                    predicates,
9267                                    semantics,
9268                                    rule_args,
9269                                    member_actions,
9270                                    return_actions,
9271                                    local_int_arg,
9272                                    member_values: member_values.clone(),
9273                                    return_values: return_values.clone(),
9274                                    rule_alt_number: next_alt_number,
9275                                    track_alt_numbers,
9276                                    consumed_eof,
9277                                    precedence,
9278                                    depth: depth + 1,
9279                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9280                                    recovery_state: epsilon_recovery_state,
9281                                },
9282                                visiting,
9283                                memo,
9284                                expected,
9285                            )
9286                            .into_iter()
9287                            .map(|mut outcome| {
9288                                prepend_decision(&mut outcome, decision);
9289                                outcome
9290                            }),
9291                        );
9292                    }
9293                }
9294                Transition::Rule {
9295                    target,
9296                    rule_index,
9297                    follow_state,
9298                    precedence: rule_precedence,
9299                    ..
9300                } => {
9301                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
9302                        continue;
9303                    };
9304                    let child_local_int_arg =
9305                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
9306                    let expected_before_child = expected.clone();
9307                    let children = self.recognize_state(
9308                        atn,
9309                        RecognizeRequest {
9310                            state_number: *target,
9311                            stop_state: child_stop,
9312                            index,
9313                            rule_start_index: index,
9314                            decision_start_index: None,
9315                            init_action_rules,
9316                            predicates,
9317                            semantics,
9318                            rule_args,
9319                            member_actions,
9320                            return_actions,
9321                            local_int_arg: child_local_int_arg,
9322                            member_values: member_values.clone(),
9323                            return_values: BTreeMap::new(),
9324                            rule_alt_number: 0,
9325                            track_alt_numbers,
9326                            consumed_eof: false,
9327                            precedence: *rule_precedence,
9328                            depth: depth + 1,
9329                            recovery_symbols: epsilon_recovery_symbols.clone(),
9330                            recovery_state: epsilon_recovery_state,
9331                        },
9332                        visiting,
9333                        memo,
9334                        expected,
9335                    );
9336                    let children = if children.is_empty() {
9337                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
9338                            atn,
9339                            rule_index: *rule_index,
9340                            start_index: index,
9341                            follow_state: *follow_state,
9342                            stop_state,
9343                            member_values: member_values.clone(),
9344                            expected,
9345                        })
9346                    } else {
9347                        children
9348                    };
9349                    let preserve_child_expected =
9350                        self.child_expected_reaches_clean_eof(&children, expected);
9351                    restore_expected(
9352                        &children,
9353                        index,
9354                        expected,
9355                        expected_before_child,
9356                        preserve_child_expected,
9357                    );
9358                    for child in children {
9359                        let child_stop_index =
9360                            self.rule_stop_token_index(child.index, child.consumed_eof);
9361                        let child_nodes = self
9362                            .recognition_arena
9363                            .fold_left_recursive_boundaries(child.nodes);
9364                        let child_node = self.arena_rule_node(ArenaRuleSpec {
9365                            rule_index: *rule_index,
9366                            invoking_state: invoking_state_number(state_number),
9367                            alt_number: child.alt_number,
9368                            start_index: index,
9369                            stop_index: child_stop_index,
9370                            return_values: child.return_values.clone(),
9371                            children: child_nodes,
9372                        });
9373                        outcomes.extend(
9374                            self.recognize_state(
9375                                atn,
9376                                RecognizeRequest {
9377                                    state_number: *follow_state,
9378                                    stop_state,
9379                                    index: child.index,
9380                                    rule_start_index,
9381                                    decision_start_index: next_decision_start_index,
9382                                    init_action_rules,
9383                                    predicates,
9384                                    semantics,
9385                                    rule_args,
9386                                    member_actions,
9387                                    return_actions,
9388                                    local_int_arg,
9389                                    member_values: child.member_values.clone(),
9390                                    return_values: return_values.clone(),
9391                                    rule_alt_number,
9392                                    track_alt_numbers,
9393                                    consumed_eof: consumed_eof || child.consumed_eof,
9394                                    precedence,
9395                                    depth: depth + 1,
9396                                    recovery_symbols: BTreeSet::new(),
9397                                    recovery_state: None,
9398                                },
9399                                visiting,
9400                                memo,
9401                                expected,
9402                            )
9403                            .into_iter()
9404                            .map(|mut outcome| {
9405                                outcome.consumed_eof |= child.consumed_eof;
9406                                outcome.diagnostics = self
9407                                    .recognition_arena
9408                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
9409                                let mut decisions = child.decisions.clone();
9410                                decisions.append(&mut outcome.decisions);
9411                                outcome.decisions = decisions;
9412                                prepend_decision(&mut outcome, decision);
9413                                let mut actions = child.actions.clone();
9414                                if init_action_rules.contains(rule_index) {
9415                                    actions.insert(
9416                                        0,
9417                                        ParserAction::new_rule_init(
9418                                            *rule_index,
9419                                            index,
9420                                            Some(*follow_state),
9421                                        ),
9422                                    );
9423                                }
9424                                actions.append(&mut outcome.actions);
9425                                outcome.actions = actions;
9426                                self.arena_prepend(&mut outcome.nodes, child_node);
9427                                outcome
9428                            }),
9429                        );
9430                    }
9431                }
9432                Transition::Atom { target, .. }
9433                | Transition::Range { target, .. }
9434                | Transition::Set { target, .. }
9435                | Transition::NotSet { target, .. }
9436                | Transition::Wildcard { target, .. } => {
9437                    let symbol = self.token_type_at(index);
9438                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
9439                        let next_index = self.consume_index(index, symbol);
9440                        outcomes.extend(
9441                            self.recognize_state(
9442                                atn,
9443                                RecognizeRequest {
9444                                    state_number: *target,
9445                                    stop_state,
9446                                    index: next_index,
9447                                    rule_start_index,
9448                                    decision_start_index: next_decision_start_index,
9449                                    init_action_rules,
9450                                    predicates,
9451                                    semantics,
9452                                    rule_args,
9453                                    member_actions,
9454                                    return_actions,
9455                                    local_int_arg,
9456                                    member_values: member_values.clone(),
9457                                    return_values: return_values.clone(),
9458                                    rule_alt_number: next_alt_number,
9459                                    track_alt_numbers,
9460                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
9461                                    precedence,
9462                                    depth: depth + 1,
9463                                    recovery_symbols: BTreeSet::new(),
9464                                    recovery_state: None,
9465                                },
9466                                visiting,
9467                                memo,
9468                                expected,
9469                            )
9470                            .into_iter()
9471                            .map(|mut outcome| {
9472                                prepend_decision(&mut outcome, decision);
9473                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9474                                let token = self.arena_token_node(index, false);
9475                                self.arena_prepend(&mut outcome.nodes, token);
9476                                outcome
9477                            }),
9478                        );
9479                    } else {
9480                        let expected_symbols =
9481                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
9482                        if expected_symbols.contains(&symbol) {
9483                            continue;
9484                        }
9485                        expected.record_transition(index, transition, atn.max_token_type());
9486                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
9487                        let before_recovery = outcomes.len();
9488                        let recovery_request = request_template.clone();
9489                        outcomes.extend(
9490                            self.single_token_deletion_recovery(RecoveryRequest {
9491                                atn,
9492                                transition,
9493                                expected_symbols: expected_symbols.clone(),
9494                                target: *target,
9495                                request: recovery_request.clone(),
9496                                visiting,
9497                                memo,
9498                                expected,
9499                            })
9500                            .into_iter()
9501                            .map(|mut outcome| {
9502                                prepend_decision(&mut outcome, decision);
9503                                outcome
9504                            }),
9505                        );
9506                        if !state_is_left_recursive_rule(atn, state) {
9507                            outcomes.extend(
9508                                self.single_token_insertion_recovery(RecoveryRequest {
9509                                    atn,
9510                                    transition,
9511                                    expected_symbols: expected_symbols.clone(),
9512                                    target: *target,
9513                                    request: recovery_request.clone(),
9514                                    visiting,
9515                                    memo,
9516                                    expected,
9517                                })
9518                                .into_iter()
9519                                .map(|mut outcome| {
9520                                    prepend_decision(&mut outcome, decision);
9521                                    outcome
9522                                }),
9523                            );
9524                        }
9525                        outcomes.extend(self.current_token_deletion_recovery(
9526                            CurrentTokenDeletionRequest {
9527                                atn,
9528                                expected_symbols: expected_symbols.clone(),
9529                                request: recovery_request.clone(),
9530                                visiting,
9531                                memo,
9532                                expected,
9533                            },
9534                        ));
9535                        if outcomes.len() == before_recovery {
9536                            outcomes.extend(self.consuming_failure_fallback(
9537                                ConsumingFailureFallback {
9538                                    atn,
9539                                    target: *target,
9540                                    request: recovery_request,
9541                                    symbol,
9542                                    expected_symbols,
9543                                    decision_start_index: next_decision_start_index,
9544                                    decision,
9545                                },
9546                                visiting,
9547                                memo,
9548                                expected,
9549                            ));
9550                        }
9551                    }
9552                }
9553            }
9554        }
9555
9556        visiting.remove(&visit_key);
9557        self.record_prediction_diagnostics(atn, state, index, &outcomes);
9558        if matches!(
9559            self.prediction_mode,
9560            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9561        ) {
9562            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
9563        }
9564        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
9565        memo.insert(key, outcomes.clone());
9566        outcomes
9567    }
9568
9569    /// Follows an epsilon or semantic-action transition while preserving the
9570    /// path-local side effects that may later become generated action output.
9571    fn recognize_epsilon_or_action_step(
9572        &mut self,
9573        atn: &Atn,
9574        request: &RecognizeRequest<'_>,
9575        step: EpsilonActionStep,
9576        scratch: RecognizeScratch<'_>,
9577    ) -> Vec<RecognizeOutcome> {
9578        let RecognizeScratch {
9579            visiting,
9580            memo,
9581            expected,
9582        } = scratch;
9583        let action = step.action_rule_index.map(|rule_index| {
9584            ParserAction::new(
9585                step.source_state,
9586                rule_index,
9587                request.rule_start_index,
9588                self.rule_stop_token_index(request.index, request.consumed_eof),
9589            )
9590        });
9591        let next_member_values = if action.is_some() {
9592            member_values_after_action(
9593                step.source_state,
9594                request.member_actions,
9595                request.semantics,
9596                &request.member_values,
9597            )
9598        } else {
9599            request.member_values.clone()
9600        };
9601        let next_return_values = action.map_or_else(
9602            || request.return_values.clone(),
9603            |action| {
9604                return_values_after_action(
9605                    step.source_state,
9606                    action.rule_index(),
9607                    request.return_actions,
9608                    request.semantics,
9609                    &request.return_values,
9610                )
9611            },
9612        );
9613
9614        self.recognize_state(
9615            atn,
9616            RecognizeRequest {
9617                state_number: step.target,
9618                stop_state: request.stop_state,
9619                index: request.index,
9620                rule_start_index: request.rule_start_index,
9621                decision_start_index: step.decision_start_index,
9622                init_action_rules: request.init_action_rules,
9623                predicates: request.predicates,
9624                semantics: request.semantics,
9625                rule_args: request.rule_args,
9626                member_actions: request.member_actions,
9627                return_actions: request.return_actions,
9628                local_int_arg: request.local_int_arg,
9629                member_values: next_member_values,
9630                return_values: next_return_values,
9631                rule_alt_number: step.alt_number,
9632                track_alt_numbers: request.track_alt_numbers,
9633                consumed_eof: request.consumed_eof,
9634                precedence: request.precedence,
9635                depth: request.depth + 1,
9636                recovery_symbols: step.recovery_symbols,
9637                recovery_state: step.recovery_state,
9638            },
9639            visiting,
9640            memo,
9641            expected,
9642        )
9643        .into_iter()
9644        .map(|mut outcome| {
9645            prepend_decision(&mut outcome, step.decision);
9646            if let Some(rule_index) = step.left_recursive_boundary {
9647                let boundary = self.arena_boundary_node(rule_index);
9648                self.arena_prepend(&mut outcome.nodes, boundary);
9649            }
9650            if let Some(action) = action {
9651                outcome.actions.insert(0, action);
9652            }
9653            outcome
9654        })
9655        .collect()
9656    }
9657
9658    /// Reads the token type at an absolute token-stream index without moving
9659    /// the parser's stream cursor. The fast recognizer probes lookahead at
9660    /// every state visit, so avoiding the seek round-trip is a measurable
9661    /// hot-path win on long inputs.
9662    fn token_type_at(&mut self, index: usize) -> i32 {
9663        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
9664            self.input.fill();
9665        }
9666        self.input.token_type_at_index(index)
9667    }
9668
9669    /// Returns the cached `state_expected_symbols` set for an ATN state.
9670    ///
9671    /// The fast recognizer consults this set on every state visit through
9672    /// `next_recovery_context`; the underlying DFS is a pure function of the
9673    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
9674    ///
9675    /// Caching is layered through `intern_recovery_symbols` so two ATN states
9676    /// with the same expected-symbol set share one `Rc`. That invariant is
9677    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
9678    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
9679    /// always interned before it ends up in a key.
9680    fn cached_state_expected_symbols(
9681        &mut self,
9682        atn: &Atn,
9683        state_number: usize,
9684    ) -> Rc<BTreeSet<i32>> {
9685        if let Some(cached) = self.state_expected_cache.get(&state_number) {
9686            return Rc::clone(cached);
9687        }
9688        let symbols = state_expected_symbols(atn, state_number);
9689        let entry = self.intern_recovery_symbols(symbols);
9690        self.state_expected_cache
9691            .insert(state_number, Rc::clone(&entry));
9692        entry
9693    }
9694
9695    fn cached_state_expected_token_set(
9696        &mut self,
9697        atn: &Atn,
9698        state_number: usize,
9699    ) -> Rc<TokenBitSet> {
9700        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
9701            return Rc::clone(cached);
9702        }
9703        // Purely a function of the ATN, so back the per-parser cache with the
9704        // thread-shared one — fresh parser instances (one per parse in
9705        // generated usage) start warm instead of rewalking the ATN.
9706        let symbols = with_shared_atn_caches(atn, |cache| {
9707            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
9708                return Rc::clone(cached);
9709            }
9710            let symbols = Rc::new(state_expected_token_set(atn, state_number));
9711            cache
9712                .state_expected_tokens
9713                .insert(state_number, Rc::clone(&symbols));
9714            symbols
9715        });
9716        self.state_expected_token_cache
9717            .insert(state_number, Rc::clone(&symbols));
9718        symbols
9719    }
9720
9721    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
9722        if self.rule_stop_reach_cache.len() <= state_number {
9723            self.rule_stop_reach_cache
9724                .resize_with(atn.states().len().max(state_number + 1), || None);
9725        }
9726        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
9727            return reaches;
9728        }
9729        let reaches = with_shared_atn_caches(atn, |cache| {
9730            *cache
9731                .rule_stop_reach
9732                .entry(state_number)
9733                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
9734        });
9735        self.rule_stop_reach_cache[state_number] = Some(reaches);
9736        reaches
9737    }
9738
9739    /// Returns the parser's empty `recovery_symbols` singleton so callers can
9740    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
9741    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
9742        Rc::clone(&self.empty_recovery_symbols)
9743    }
9744
9745    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
9746    /// recognizer can hash and compare keys by pointer.
9747    ///
9748    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
9749    /// come from this method or the empty singleton; otherwise two
9750    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
9751    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
9752    /// recognition coordinates.
9753    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
9754        if set.is_empty() {
9755            return Rc::clone(&self.empty_recovery_symbols);
9756        }
9757        let candidate = Rc::new(set);
9758        match self.recovery_symbols_intern.get(&candidate) {
9759            Some(existing) => Rc::clone(existing),
9760            None => {
9761                self.recovery_symbols_intern
9762                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
9763                candidate
9764            }
9765        }
9766    }
9767
9768    /// Returns the cached look-1 entry for a decision state, computing it on
9769    /// first use. Multi-alternative states are visited many times during
9770    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
9771    /// hash lookup per visit.
9772    fn cached_decision_lookahead(
9773        &mut self,
9774        atn: &Atn,
9775        state: AtnState<'_>,
9776        rule_stop_state: usize,
9777    ) -> Rc<DecisionLookahead> {
9778        // Hit the parser-instance cache first. Decision lookahead is purely
9779        // a function of the ATN/state, so on a warm cache we skip the
9780        // thread-local + RefCell + HashMap-entry dance through
9781        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
9782        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
9783        // hashmap-entry overhead in profiles.
9784        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
9785            return Rc::clone(cached);
9786        }
9787        let entry = with_shared_atn_caches(atn, |cache| {
9788            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
9789                return Rc::clone(cached);
9790            }
9791            let mut entry = DecisionLookahead {
9792                transitions: Vec::with_capacity(state.transitions().len()),
9793            };
9794            for transition in &state.transitions() {
9795                entry.transitions.push(transition_first_set(
9796                    atn,
9797                    transition,
9798                    rule_stop_state,
9799                    &mut cache.first_set,
9800                ));
9801            }
9802            let entry = Rc::new(entry);
9803            cache
9804                .decision_lookahead
9805                .insert(state.state_number(), Rc::clone(&entry));
9806            entry
9807        });
9808        self.decision_lookahead_cache
9809            .insert(state.state_number(), Rc::clone(&entry));
9810        entry
9811    }
9812
9813    fn cached_rule_first_set(
9814        &mut self,
9815        atn: &Atn,
9816        target: usize,
9817        child_stop: usize,
9818    ) -> Rc<FirstSet> {
9819        if self.rule_first_set_cache.len() <= target {
9820            self.rule_first_set_cache
9821                .resize_with(atn.states().len().max(target + 1), || None);
9822        }
9823        if let Some(cached) = self
9824            .rule_first_set_cache
9825            .get(target)
9826            .and_then(Option::as_ref)
9827        {
9828            return Rc::clone(cached);
9829        }
9830        let first = with_shared_first_set_cache(atn, |cache| {
9831            rule_first_set(atn, target, child_stop, cache)
9832        });
9833        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
9834        first
9835    }
9836
9837    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
9838        let atn_key = SharedAtnCacheKey::for_atn(atn);
9839        if self.empty_cycle_cache_atn != Some(atn_key) {
9840            self.empty_cycle_cache.clear();
9841            self.empty_cycle_cache_atn = Some(atn_key);
9842        }
9843        if self.empty_cycle_cache.len() <= state_number {
9844            self.empty_cycle_cache
9845                .resize_with(atn.state_count().max(state_number + 1), || None);
9846        }
9847        if let Some(cached) = self.empty_cycle_cache[state_number] {
9848            return cached;
9849        }
9850        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
9851        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
9852        self.empty_cycle_cache[state_number] = Some(result);
9853        result
9854    }
9855
9856    fn empty_path_reaches_state(
9857        &mut self,
9858        atn: &Atn,
9859        state_number: usize,
9860        target_state: usize,
9861        visited: &mut FxHashSet<usize>,
9862    ) -> bool {
9863        if !visited.insert(state_number) {
9864            return false;
9865        }
9866        let Some(state) = atn.state(state_number) else {
9867            return false;
9868        };
9869        for transition in &state.transitions() {
9870            let kind = transition.kind();
9871            let target = transition.target();
9872            match kind {
9873                ParserTransitionKind::Atom
9874                | ParserTransitionKind::Range
9875                | ParserTransitionKind::Set
9876                | ParserTransitionKind::NotSet
9877                | ParserTransitionKind::Wildcard => {}
9878                ParserTransitionKind::Rule => {
9879                    let rule_index = transition.arg0() as usize;
9880                    let follow_state = transition.arg1() as usize;
9881                    if target == target_state
9882                        || self.empty_path_reaches_state(atn, target, target_state, visited)
9883                    {
9884                        return true;
9885                    }
9886                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
9887                        continue;
9888                    };
9889                    if self.cached_rule_first_set(atn, target, child_stop).nullable
9890                        && (follow_state == target_state
9891                            || self.empty_path_reaches_state(
9892                                atn,
9893                                follow_state,
9894                                target_state,
9895                                visited,
9896                            ))
9897                    {
9898                        return true;
9899                    }
9900                }
9901                ParserTransitionKind::Epsilon
9902                | ParserTransitionKind::Predicate
9903                | ParserTransitionKind::Action
9904                | ParserTransitionKind::Precedence => {
9905                    if target == target_state
9906                        || self.empty_path_reaches_state(atn, target, target_state, visited)
9907                    {
9908                        return true;
9909                    }
9910                }
9911            }
9912        }
9913        false
9914    }
9915
9916    /// Decides whether the clean recognizer should use its full outcome memo
9917    /// table for this coordinate.
9918    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
9919        match self.clean_memo_mode {
9920            CleanMemoMode::Promote => true,
9921            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
9922            CleanMemoMode::Sparse => {
9923                self.clean_memo_sparse_samples += 1;
9924                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
9925                    return false;
9926                }
9927                self.clean_memo_sparse_samples = 0;
9928                self.clean_memo_mode = CleanMemoMode::Probe;
9929                self.clean_memo_probe_samples = 0;
9930                self.clean_memo_probe_repeats = 0;
9931                self.clean_memo_probe_seen.clear();
9932                self.observe_clean_memo_probe(key)
9933            }
9934        }
9935    }
9936
9937    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
9938        self.clean_memo_probe_samples += 1;
9939        if !self.clean_memo_probe_seen.insert(key.clone()) {
9940            self.clean_memo_probe_repeats += 1;
9941        }
9942        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
9943            self.clean_memo_mode = CleanMemoMode::Promote;
9944            self.clean_memo_probe_seen.clear();
9945            return true;
9946        }
9947        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
9948            self.clean_memo_mode = CleanMemoMode::Sparse;
9949            self.clean_memo_sparse_samples = 0;
9950            self.clean_memo_probe_seen.clear();
9951            return false;
9952        }
9953        true
9954    }
9955
9956    /// Borrows the visible token at an absolute token-stream index.
9957    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
9958        self.input.get(index)
9959    }
9960
9961    /// Returns the compact token ID at an absolute token-stream index.
9962    fn token_id_at(&self, index: usize) -> Option<TokenId> {
9963        self.input.get_id(index)
9964    }
9965
9966    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
9967        let token = self
9968            .token_id_at(index)
9969            .expect("recognized token index must exist in the token store");
9970        let node = if error {
9971            ArenaRecognizedNode::ErrorToken { token }
9972        } else {
9973            ArenaRecognizedNode::Token { token }
9974        };
9975        self.recognition_arena.push_node(node)
9976    }
9977
9978    fn arena_missing_token_node(
9979        &mut self,
9980        token_type: i32,
9981        at_index: usize,
9982        text: String,
9983    ) -> RecognizedNodeId {
9984        let extra = self
9985            .recognition_arena
9986            .push_extra(RecognitionExtra::MissingToken {
9987                token_type,
9988                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
9989                text,
9990            });
9991        self.recognition_arena
9992            .push_node(ArenaRecognizedNode::MissingToken { extra })
9993    }
9994
9995    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
9996        let ArenaRuleSpec {
9997            rule_index,
9998            invoking_state,
9999            alt_number,
10000            start_index,
10001            stop_index,
10002            return_values,
10003            children,
10004        } = spec;
10005        let return_values = (!return_values.is_empty()).then(|| {
10006            self.recognition_arena
10007                .push_extra(RecognitionExtra::ReturnValues(return_values))
10008        });
10009        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
10010            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10011            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
10012            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10013            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
10014            stop_index: stop_index
10015                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
10016            return_values,
10017            children,
10018        })
10019    }
10020
10021    fn arena_boundary_node(&mut self, rule_index: usize) -> RecognizedNodeId {
10022        self.recognition_arena
10023            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
10024                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10025            })
10026    }
10027
10028    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
10029        *sequence = self.recognition_arena.prepend(*sequence, node);
10030    }
10031
10032    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
10033        self.last_recognition_arena_root = root;
10034        self.last_recognition_arena_diagnostics = diagnostics;
10035        #[cfg(feature = "perf-counters")]
10036        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
10037            let stats = self.recognition_arena_stats();
10038            #[allow(clippy::print_stderr)]
10039            {
10040                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
10041                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
10042                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
10043                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
10044                eprintln!("perf recognition_links_total={}", stats.total_links);
10045                eprintln!("perf recognition_links_live={}", stats.live_links);
10046                eprintln!("perf recognition_links_dead={}", stats.dead_links);
10047                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
10048                eprintln!("perf recognition_extras_total={}", stats.total_extras);
10049                eprintln!("perf recognition_extras_live={}", stats.live_extras);
10050                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
10051                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
10052            }
10053        }
10054    }
10055
10056    fn reset_recognition_arena(&mut self) {
10057        self.recognition_arena.reset();
10058        self.last_recognition_arena_root = NodeSeqId::EMPTY;
10059        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
10060    }
10061
10062    /// Normalizes the current token-stream cursor to the next parser-visible
10063    /// token before capturing a rule start boundary.
10064    fn current_visible_index(&mut self) -> usize {
10065        let index = self.input.index();
10066        self.input.seek(index);
10067        self.input.index()
10068    }
10069
10070    /// Reports whether a child rule reached EOF cleanly while also recording
10071    /// an EOF expectation from a longer path inside that child.
10072    fn child_expected_reaches_clean_eof(
10073        &mut self,
10074        children: &[RecognizeOutcome],
10075        expected: &ExpectedTokens,
10076    ) -> bool {
10077        let Some(index) = expected.index else {
10078            return false;
10079        };
10080        self.token_type_at(index) == TOKEN_EOF
10081            && children
10082                .iter()
10083                .any(|child| child.diagnostics.is_empty() && child.index == index)
10084    }
10085
10086    /// Finds the previous token visible to the parser before `index`.
10087    ///
10088    /// The token stream cursor skips hidden-channel tokens, so subtracting one
10089    /// from a visible-token index can point at whitespace. Parser intervals use
10090    /// this helper to stop at the previous visible token while preserving hidden
10091    /// text inside the rendered interval.
10092    fn previous_token_index(&self, index: usize) -> Option<usize> {
10093        self.input.previous_visible_token_index(index)
10094    }
10095
10096    /// Returns the token-stream index used as a rule stop boundary.
10097    ///
10098    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
10099    /// stop at `index` rather than at the previous visible token.
10100    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
10101        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
10102            Some(index)
10103        } else {
10104            self.previous_token_index(index)
10105        }
10106    }
10107
10108    /// Stop-token index for a rule's `@after` action, matching the boundary that
10109    /// `finish_rule` records on the rule context.
10110    ///
10111    /// A rule that matched EOF leaves the cursor parked on the EOF token
10112    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
10113    /// the current index rather than the previous visible token. Without this,
10114    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
10115    /// report the token before EOF (or `None` for empty input), diverging from
10116    /// the rule context that `finish_rule` builds.
10117    ///
10118    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
10119    /// rule ends right before EOF without matching it (the cursor is parked on
10120    /// EOF, but the rule did not consume it). Prefer
10121    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
10122    /// rule context already recorded with the real flag. Kept for callers without
10123    /// the rule tree in hand.
10124    #[must_use]
10125    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
10126        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
10127        self.rule_stop_token_index(current_index, consumed_eof)
10128    }
10129
10130    /// Stop-token index for a rule's `@after` action, taken from the stop token
10131    /// the rule context already recorded.
10132    ///
10133    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
10134    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
10135    /// the rule context — even when the rule ends immediately before EOF without
10136    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
10137    /// to the cursor-based inference only when the tree carries no rule stop.
10138    #[must_use]
10139    pub fn after_action_stop_index_for_tree(
10140        &mut self,
10141        tree: ParseTree,
10142        current_index: usize,
10143    ) -> Option<usize> {
10144        if let Some(stop) = self
10145            .node(tree)
10146            .as_rule()
10147            .and_then(crate::tree::RuleNodeView::stop_id)
10148        {
10149            return Some(stop.index());
10150        }
10151        self.after_action_stop_index(current_index)
10152    }
10153
10154    /// Start-token index for a rule's `@after` action, taken from the start token
10155    /// the rule context already recorded.
10156    ///
10157    /// `enter_rule` sets the rule context start to the first visible token (it
10158    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
10159    /// `$text` in an `@after` action aligned with the rule context — even when the
10160    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
10161    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
10162    /// the tree carries no rule start.
10163    #[must_use]
10164    pub fn after_action_start_index_for_tree(
10165        &self,
10166        tree: ParseTree,
10167        fallback_index: usize,
10168    ) -> usize {
10169        if let Some(start) = self
10170            .node(tree)
10171            .as_rule()
10172            .and_then(crate::tree::RuleNodeView::start_id)
10173        {
10174            return start.index();
10175        }
10176        fallback_index
10177    }
10178
10179    /// Returns the rule stop token for a selected parse path.
10180    ///
10181    /// EOF transitions do not advance the token-stream cursor, so an EOF match
10182    /// must use the current token rather than the previous visible token.
10183    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
10184        self.rule_stop_token_index(index, consumed_eof)
10185            .and_then(|token_index| self.token_id_at(token_index))
10186    }
10187
10188    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
10189    ///
10190    /// Generated Java reports the failed-predicate message at the current
10191    /// lookahead, then consumes until rule recovery can resume. The metadata
10192    /// runtime models the same visible tree shape by keeping skipped tokens as
10193    /// error nodes and returning from the active rule at EOF.
10194    fn predicate_failure_recovery(
10195        &mut self,
10196        request: PredicateFailureRecovery<'_>,
10197    ) -> RecognizeOutcome {
10198        let PredicateFailureRecovery {
10199            rule_index,
10200            index,
10201            message,
10202            member_values,
10203            return_values,
10204            rule_alt_number,
10205        } = request;
10206        let rule_name = self
10207            .rule_names()
10208            .get(rule_index)
10209            .map_or_else(|| rule_index.to_string(), Clone::clone);
10210        let diagnostic = diagnostic_for_token(
10211            self.token_at(index).as_ref(),
10212            format!("rule {rule_name} {message}"),
10213        );
10214        let mut reversed_nodes = NodeSeqId::EMPTY;
10215        let mut next_index = index;
10216        loop {
10217            let symbol = self.token_type_at(next_index);
10218            if symbol == TOKEN_EOF {
10219                break;
10220            }
10221            let error = self.arena_token_node(next_index, true);
10222            self.arena_prepend(&mut reversed_nodes, error);
10223            let after = self.consume_index(next_index, symbol);
10224            if after == next_index {
10225                break;
10226            }
10227            next_index = after;
10228        }
10229        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
10230        let diagnostics = self
10231            .recognition_arena
10232            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10233        RecognizeOutcome {
10234            index: next_index,
10235            consumed_eof: false,
10236            alt_number: rule_alt_number,
10237            member_values,
10238            return_values,
10239            diagnostics,
10240            decisions: Vec::new(),
10241            actions: Vec::new(),
10242            nodes,
10243        }
10244    }
10245
10246    /// Evaluates a user hook for a predicate coordinate that has no generated
10247    /// runtime table entry.
10248    fn parser_semantic_hook_result(
10249        &mut self,
10250        request: ParserSemanticHookRequest<'_>,
10251    ) -> Option<bool> {
10252        let ParserSemanticHookRequest {
10253            index,
10254            rule_index,
10255            pred_index,
10256            context,
10257            local_int_arg,
10258            member_values,
10259        } = request;
10260        let rule_name = self.rule_names().get(rule_index).cloned();
10261        self.input.seek(index);
10262        let input = &mut self.input;
10263        let semantic_hooks = &mut self.semantic_hooks;
10264        let mut ctx = ParserSemCtx {
10265            input,
10266            tree_storage: &self.tree,
10267            rule_index,
10268            coordinate_index: pred_index,
10269            rule_name,
10270            context,
10271            tree: None,
10272            local_int_arg,
10273            member_values,
10274            action: None,
10275        };
10276        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
10277    }
10278
10279    /// Re-inserts unknown-predicate coordinates recorded before a nested
10280    /// interpreted recognition, preserving order and skipping any the nested
10281    /// call already recorded, so a generated parent's fail-loud coordinates
10282    /// survive descending into an interpreted child.
10283    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
10284        if prior.is_empty() {
10285            return;
10286        }
10287        let mut merged = prior;
10288        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
10289            if !merged.contains(&coordinate) {
10290                merged.push(coordinate);
10291            }
10292        }
10293        self.unknown_predicate_hits = merged;
10294    }
10295
10296    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
10297    /// that has no entry in the generated predicate table.
10298    ///
10299    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
10300    /// the guarded path is abandoned; the parse entry surfaces the recorded
10301    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
10302    /// because a parse that consulted an unknown predicate is unreliable no
10303    /// matter which paths were ultimately selected.
10304    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
10305        apply_unknown_predicate_policy(
10306            self.unknown_predicate_policy,
10307            rule_index,
10308            pred_index,
10309            &mut self.unknown_predicate_hits,
10310        )
10311    }
10312
10313    /// Builds the fail-loud error for unknown predicate coordinates recorded
10314    /// by the current parse, if any.
10315    fn unknown_semantic_error(&self) -> Option<AntlrError> {
10316        use std::fmt::Write as _;
10317        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
10318            return None;
10319        }
10320        let mut message = String::new();
10321        for (rule_index, pred_index) in &self.unknown_predicate_hits {
10322            if !message.is_empty() {
10323                message.push_str("; ");
10324            }
10325            let _ = match self.rule_names().get(*rule_index) {
10326                Some(rule_name) => write!(
10327                    message,
10328                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
10329                ),
10330                None => write!(
10331                    message,
10332                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
10333                ),
10334            };
10335        }
10336        for (rule_index, source_state) in &self.unhandled_action_hits {
10337            if !message.is_empty() {
10338                message.push_str("; ");
10339            }
10340            let _ = match self.rule_names().get(*rule_index) {
10341                Some(rule_name) => write!(
10342                    message,
10343                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
10344                ),
10345                None => write!(
10346                    message,
10347                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
10348                ),
10349            };
10350        }
10351        Some(AntlrError::Unsupported(message))
10352    }
10353
10354    /// Evaluates one lowered predicate expression at the requested input
10355    /// position.
10356    ///
10357    /// This sits in the prediction hot loop, so the context borrows the
10358    /// speculative member state read-only and the rule name by reference —
10359    /// no per-evaluation allocation. Only the hook escape path materializes
10360    /// owned copies, and only when a hook is actually consulted.
10361    fn parser_semir_predicate_matches(
10362        &mut self,
10363        semantics: &ParserSemantics,
10364        predicate: &ParserSemanticPredicate,
10365        request: ParserSemanticHookRequest<'_>,
10366    ) -> bool {
10367        self.input.seek(request.index);
10368        let rule_name = self
10369            .data
10370            .rule_names()
10371            .get(request.rule_index)
10372            .map(String::as_str);
10373        let unknown_predicate_policy = self.unknown_predicate_policy;
10374        let mut ctx = ParserSemIrCtx {
10375            input: &mut self.input,
10376            tree_storage: &self.tree,
10377            semantic_hooks: &mut self.semantic_hooks,
10378            rule_index: request.rule_index,
10379            coordinate_index: request.pred_index,
10380            rule_name,
10381            context: request.context,
10382            local_int_arg: request.local_int_arg,
10383            member_values: request.member_values,
10384            invoked_predicates: &mut self.invoked_predicates,
10385            unknown_predicate_policy,
10386            unknown_predicate_hits: &mut self.unknown_predicate_hits,
10387        };
10388        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
10389    }
10390
10391    fn fast_parser_predicate_matches(
10392        &mut self,
10393        context: Option<FastPredicateContext<'_>>,
10394        transition: ParserTransition<'_>,
10395        index: usize,
10396    ) -> bool {
10397        let Some(context) = context else {
10398            return true;
10399        };
10400        let rule_index = transition.arg0() as usize;
10401        let pred_index = transition.arg1() as usize;
10402        let key = (index, rule_index, pred_index);
10403        if let Some(result) = self.fast_predicate_cache.get(&key) {
10404            return *result;
10405        }
10406        let result = self.parser_predicate_matches(PredicateEval {
10407            index,
10408            rule_index,
10409            pred_index,
10410            predicates: context.predicates,
10411            semantics: context.semantics,
10412            context: None,
10413            local_int_arg: None,
10414            member_values: context.member_values,
10415        });
10416        self.fast_predicate_cache.insert(key, result);
10417        result
10418    }
10419
10420    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
10421        let PredicateEval {
10422            index,
10423            rule_index,
10424            pred_index,
10425            predicates,
10426            semantics,
10427            context,
10428            local_int_arg,
10429            member_values,
10430        } = eval;
10431        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
10432            semantics
10433                .predicates
10434                .iter()
10435                .find(|predicate| {
10436                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
10437                })
10438                .map(|predicate| (semantics, predicate))
10439        }) {
10440            return self.parser_semir_predicate_matches(
10441                semantics,
10442                predicate,
10443                ParserSemanticHookRequest {
10444                    index,
10445                    rule_index,
10446                    pred_index,
10447                    context,
10448                    local_int_arg,
10449                    member_values,
10450                },
10451            );
10452        }
10453        let Some((_, _, predicate)) = predicates
10454            .iter()
10455            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
10456        else {
10457            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
10458                index,
10459                rule_index,
10460                pred_index,
10461                context,
10462                local_int_arg,
10463                member_values,
10464            }) {
10465                return result;
10466            }
10467            return self.unknown_predicate_result(rule_index, pred_index);
10468        };
10469        self.input.seek(index);
10470        match predicate {
10471            ParserPredicate::True => true,
10472            ParserPredicate::False => false,
10473            ParserPredicate::FalseWithMessage { .. } => false,
10474            ParserPredicate::Invoke { value } => {
10475                let key = (rule_index, pred_index);
10476                if !self.invoked_predicates.contains(&key) {
10477                    self.invoked_predicates.push(key);
10478                    use std::io::Write as _;
10479                    let mut stdout = std::io::stdout().lock();
10480                    let _ = writeln!(stdout, "eval={value}");
10481                }
10482                *value
10483            }
10484            ParserPredicate::LookaheadTextEquals { offset, text } => self
10485                .input
10486                .lt(*offset)
10487                .is_some_and(|token| Token::text(&token) == Some(*text)),
10488            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
10489                self.la(*offset) != *token_type
10490            }
10491            ParserPredicate::TokenPairAdjacent => {
10492                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
10493                    return false;
10494                };
10495                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
10496                    return false;
10497                };
10498                first + 1 == second
10499            }
10500            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
10501                .and_then(|context| {
10502                    context
10503                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
10504                        .next()
10505                        .map(crate::tree::RuleNodeView::text)
10506                })
10507                .is_none_or(|actual| actual != *text),
10508            ParserPredicate::LocalIntEquals { value } => {
10509                local_int_arg.is_none_or(|(_, actual)| actual == *value)
10510            }
10511            ParserPredicate::LocalIntLessOrEqual { value } => {
10512                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
10513            }
10514            ParserPredicate::MemberModuloEquals {
10515                member,
10516                modulus,
10517                value,
10518                equals,
10519            } => {
10520                if *modulus == 0 {
10521                    return false;
10522                }
10523                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
10524                (actual == *value) == *equals
10525            }
10526            ParserPredicate::MemberEquals {
10527                member,
10528                value,
10529                equals,
10530            } => {
10531                let actual = member_values.get(member).copied().unwrap_or_default();
10532                (actual == *value) == *equals
10533            }
10534        }
10535    }
10536
10537    /// Returns a generated fail-option message for a predicate coordinate.
10538    fn parser_predicate_failure_message(
10539        &self,
10540        rule_index: usize,
10541        pred_index: usize,
10542        predicates: &[(usize, usize, ParserPredicate)],
10543    ) -> Option<&'static str> {
10544        predicates
10545            .iter()
10546            .find_map(|(rule, pred, predicate)| match predicate {
10547                ParserPredicate::FalseWithMessage { message }
10548                    if *rule == rule_index && *pred == pred_index =>
10549                {
10550                    Some(*message)
10551                }
10552                _ => None,
10553            })
10554    }
10555
10556    /// Returns a generated fail-option message for a `SemIR` predicate
10557    /// coordinate.
10558    pub fn parser_semantic_ir_predicate_failure_message(
10559        &self,
10560        rule_index: usize,
10561        pred_index: usize,
10562        semantics: &ParserSemantics,
10563    ) -> Option<&'static str> {
10564        semantics
10565            .predicates
10566            .iter()
10567            .find(|predicate| {
10568                predicate.rule_index == rule_index && predicate.pred_index == pred_index
10569            })
10570            .and_then(|predicate| predicate.failure_message)
10571    }
10572
10573    /// Returns the token-stream index after consuming `symbol` at `index`.
10574    ///
10575    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
10576    /// index stable and rely on `consumed_eof` to record that EOF was matched.
10577    /// The parser's stream cursor is left untouched: speculative recognition
10578    /// reads ahead by absolute index, so paying for `seek` on every visited
10579    /// state would dominate the hot path. Real consumption is committed by
10580    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
10581    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
10582        if symbol == TOKEN_EOF {
10583            return index;
10584        }
10585        self.input.next_visible_after(index)
10586    }
10587
10588    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
10589    /// decision that failed after consuming a shared prefix.
10590    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
10591        let text = display_input_text(&self.input.text(start_index, error_index));
10592        diagnostic_for_token(
10593            self.token_at(error_index).as_ref(),
10594            format!("no viable alternative at input '{text}'"),
10595        )
10596    }
10597
10598    /// Selects the diagnostic for a failed consuming transition after all
10599    /// recovery repairs have been ruled out.
10600    fn recovery_failure_diagnostic(
10601        &self,
10602        index: usize,
10603        decision_start_index: Option<usize>,
10604        expected_symbols: &BTreeSet<i32>,
10605    ) -> ParserDiagnostic {
10606        if expected_symbols.len() > 1 {
10607            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
10608                return self.no_viable_alternative(decision_start, index);
10609            }
10610        }
10611        diagnostic_for_token(
10612            self.token_at(index).as_ref(),
10613            format!(
10614                "mismatched input {} expecting {}",
10615                self.token_at(index)
10616                    .as_ref()
10617                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10618                self.expected_symbols_display(expected_symbols)
10619            ),
10620        )
10621    }
10622
10623    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
10624    /// instead of inserting missing tokens in the caller.
10625    fn eof_rule_recovery_diagnostic(
10626        &self,
10627        index: usize,
10628        expected_symbols: &BTreeSet<i32>,
10629        expected: &ExpectedTokens,
10630    ) -> ParserDiagnostic {
10631        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
10632            &expected.symbols
10633        } else {
10634            expected_symbols
10635        };
10636        diagnostic_for_token(
10637            self.token_at(index).as_ref(),
10638            format!(
10639                "mismatched input {} expecting {}",
10640                self.token_at(index)
10641                    .as_ref()
10642                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10643                self.expected_symbols_display(symbols)
10644            ),
10645        )
10646    }
10647
10648    /// Returns token text for a buffered token interval used by generated
10649    /// `$text` actions.
10650    ///
10651    /// ANTLR treats EOF as a range boundary rather than printable input text,
10652    /// even when an action interval explicitly stops at the EOF token.
10653    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
10654        let Some(stop) = stop else {
10655            return String::new();
10656        };
10657        let stop = if self
10658            .token_at(stop)
10659            .is_some_and(|token| token.token_type() == TOKEN_EOF)
10660        {
10661            let Some(previous) = self.previous_token_index(stop) else {
10662                return String::new();
10663            };
10664            previous
10665        } else {
10666            stop
10667        };
10668        self.input.text(start, stop)
10669    }
10670
10671    /// Resets per-parse prediction diagnostics while keeping the parser-level
10672    /// reporting flag configured by generated harness code.
10673    fn clear_prediction_diagnostics(&mut self) {
10674        self.prediction_diagnostics.clear();
10675        self.reported_prediction_diagnostics.clear();
10676    }
10677
10678    /// Drops every per-parse cache that depends on ATN identity or pins
10679    /// recovery-symbol allocations.
10680    ///
10681    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
10682    /// same parser instance can legally be driven against different grammars
10683    /// in sequence. The four caches reset here are keyed by raw ATN
10684    /// coordinates (state numbers, rule indexes) and would silently hand back
10685    /// entries from a previous ATN if reused — pruning lookahead against the
10686    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
10687    /// for the rest of the process. Clearing them on every parse entry keeps
10688    /// the perf wins (caches still amortize within one parse) without making
10689    /// long-lived parsers leak memory or surface stale ATN data:
10690    ///
10691    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
10692    ///   functions of the ATN's state graph.
10693    /// * `state_expected_cache`, `state_expected_token_cache`,
10694    ///   `rule_stop_reach_cache`, and
10695    ///   `recovery_symbols_intern` together form
10696    ///   the identity invariant that lets `FastRecognizeKey` hash
10697    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
10698    ///   so a stale interned `Rc` cannot outlive its map entry.
10699    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
10700    ///   it is retained here and invalidated lazily when the ATN changes.
10701    fn reset_per_parse_caches(&mut self) {
10702        self.rule_first_set_cache.clear();
10703        self.decision_lookahead_cache.clear();
10704        self.ll1_decision_cache.clear();
10705        self.fast_predicate_cache.clear();
10706        self.rule_stop_reach_cache.clear();
10707        self.clean_memo_mode = CleanMemoMode::Probe;
10708        self.clean_memo_probe_seen.clear();
10709        self.clean_memo_probe_samples = 0;
10710        self.clean_memo_probe_repeats = 0;
10711        self.clean_memo_sparse_samples = 0;
10712        self.recovery_symbols_intern.clear();
10713        self.state_expected_cache.clear();
10714        self.state_expected_token_cache.clear();
10715    }
10716
10717    /// Buffers ANTLR-style diagnostic-listener messages for decision states
10718    /// where multiple clean alternatives survive full-context recognition.
10719    fn record_prediction_diagnostics(
10720        &mut self,
10721        atn: &Atn,
10722        state: AtnState<'_>,
10723        start_index: usize,
10724        outcomes: &[RecognizeOutcome],
10725    ) {
10726        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
10727            return;
10728        }
10729        let Some(decision) = atn
10730            .decision_to_state()
10731            .iter()
10732            .position(|state_number| state_number == state.state_number())
10733        else {
10734            return;
10735        };
10736        let Some(rule_index) = state.rule_index() else {
10737            return;
10738        };
10739        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
10740        for outcome in outcomes
10741            .iter()
10742            .filter(|outcome| outcome.diagnostics.is_empty())
10743        {
10744            let Some(alt) = outcome.decisions.first() else {
10745                continue;
10746            };
10747            alts_by_end
10748                .entry(outcome.index)
10749                .or_default()
10750                .insert(alt + 1);
10751        }
10752        let Some((&end_index, ambig_alts)) = alts_by_end
10753            .iter()
10754            .filter(|(_, alts)| alts.len() > 1)
10755            .max_by_key(|(end, _)| *end)
10756        else {
10757            return;
10758        };
10759        let rule_name = self
10760            .rule_names()
10761            .get(rule_index)
10762            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
10763        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
10764        let input = display_input_text(&self.input.text(start_index, stop_index));
10765        let alts = ambig_alts
10766            .iter()
10767            .map(usize::to_string)
10768            .collect::<Vec<_>>()
10769            .join(", ");
10770        let key = (decision, start_index, format!("{alts}:{input}"));
10771        if !self.reported_prediction_diagnostics.insert(key) {
10772            return;
10773        }
10774        let start_diagnostic = diagnostic_for_token(
10775            self.token_at(start_index),
10776            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
10777        );
10778        let stop_diagnostic = diagnostic_for_token(
10779            self.token_at(stop_index),
10780            format!(
10781                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
10782            ),
10783        );
10784        self.prediction_diagnostics.push(start_diagnostic);
10785        self.prediction_diagnostics.push(stop_diagnostic);
10786    }
10787
10788    /// Formats the tokens expected from an ATN state using ANTLR display names.
10789    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
10790        expected_symbols_display(
10791            &state_expected_symbols(atn, state_number),
10792            self.vocabulary(),
10793        )
10794    }
10795
10796    /// Expected-token set at the parser's current ATN state — ANTLR's
10797    /// `getExpectedTokens()`. Generated recognizers expose this as
10798    /// `self.expected_tokens()` for embedded test actions
10799    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
10800    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
10801        let state = usize::try_from(self.data().state()).unwrap_or(0);
10802        ExpectedTokenSet {
10803            symbols: state_expected_symbols(atn, state),
10804        }
10805    }
10806
10807    /// Enables the bail error strategy: the first syntax error aborts the
10808    /// parse instead of recovering.
10809    pub const fn set_bail_on_error(&mut self, bail: bool) {
10810        self.bail_on_error = bail;
10811    }
10812
10813    /// Whether the bail error strategy is active.
10814    #[must_use]
10815    pub const fn bail_on_error(&self) -> bool {
10816        self.bail_on_error
10817    }
10818
10819    /// Names of the rules on the live invocation stack, current rule first —
10820    /// ANTLR's `getRuleInvocationStack()`.
10821    pub fn rule_invocation_stack(&self) -> Vec<String> {
10822        self.rule_context_stack
10823            .iter()
10824            .rev()
10825            .map(|frame| {
10826                self.data()
10827                    .rule_names()
10828                    .get(frame.rule_index)
10829                    .cloned()
10830                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
10831            })
10832            .collect()
10833    }
10834
10835    /// Invoking-state chain for the active rule context, current rule first.
10836    ///
10837    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
10838    pub fn active_invocation_states(&self) -> Vec<isize> {
10839        self.rule_context_stack
10840            .iter()
10841            .skip(1)
10842            .rev()
10843            .map(|frame| frame.invoking_state)
10844            .collect()
10845    }
10846
10847    /// Formats a buffered token in ANTLR's diagnostic token display form.
10848    pub fn token_display_at(&self, index: usize) -> Option<String> {
10849        self.token_at(index).map(|token| format!("{token}"))
10850    }
10851}
10852
10853impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
10854where
10855    S: TokenSource,
10856    H: SemanticHooks,
10857{
10858    fn parse_rule(
10859        &mut self,
10860        rule_index: usize,
10861        invoking_state: isize,
10862        precedence: i32,
10863    ) -> DirectAdaptiveParseResult<ParseTree> {
10864        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
10865            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
10866        )?;
10867        let stop_state = self
10868            .atn
10869            .rule_to_stop_state()
10870            .get(rule_index)
10871            .filter(|state| *state != usize::MAX)
10872            .ok_or(DirectAdaptiveParseControl::Fallback(
10873                DirectAdaptiveFallback::MissingAtn,
10874            ))?;
10875        let start_index = self.parser.current_visible_index();
10876        let mut context = ParserRuleContext::new(rule_index, invoking_state);
10877        if let Some(token) = self.parser.token_id_at(start_index) {
10878            self.parser.set_context_start(&mut context, token);
10879        }
10880        let mut state_number = start_state;
10881        let mut consumed_eof = false;
10882        while state_number != stop_state {
10883            self.step()?;
10884            let (transition, boundary) = self.next_transition(state_number, precedence)?;
10885            if boundary.is_some() {
10886                return Err(DirectAdaptiveParseControl::Fallback(
10887                    DirectAdaptiveFallback::LeftRecursiveBoundary,
10888                ));
10889            }
10890            match transition.data() {
10891                Transition::Epsilon { target } => {
10892                    state_number = target;
10893                }
10894                Transition::Precedence {
10895                    target,
10896                    precedence: transition_precedence,
10897                } => {
10898                    if transition_precedence < precedence {
10899                        return Err(DirectAdaptiveParseControl::Fallback(
10900                            DirectAdaptiveFallback::Precedence,
10901                        ));
10902                    }
10903                    state_number = target;
10904                }
10905                Transition::Rule {
10906                    rule_index,
10907                    follow_state,
10908                    precedence: rule_precedence,
10909                    ..
10910                } => {
10911                    let child = self.parse_rule(
10912                        rule_index,
10913                        invoking_state_number(state_number),
10914                        rule_precedence,
10915                    )?;
10916                    if self.parser.build_parse_trees {
10917                        self.parser.tree.add_child(&mut context, child);
10918                    }
10919                    state_number = follow_state;
10920                }
10921                Transition::Atom { .. }
10922                | Transition::Range { .. }
10923                | Transition::Set { .. }
10924                | Transition::NotSet { .. }
10925                | Transition::Wildcard { .. } => {
10926                    let (matched_eof, child) = self.consume_transition(transition)?;
10927                    consumed_eof |= matched_eof;
10928                    if let Some(child) = child {
10929                        self.parser.tree.add_child(&mut context, child);
10930                    }
10931                    state_number = transition.target();
10932                }
10933                Transition::Predicate { .. } => {
10934                    return Err(DirectAdaptiveParseControl::Fallback(
10935                        DirectAdaptiveFallback::Predicate,
10936                    ));
10937                }
10938                Transition::Action { .. } => {
10939                    return Err(DirectAdaptiveParseControl::Fallback(
10940                        DirectAdaptiveFallback::Action,
10941                    ));
10942                }
10943            }
10944        }
10945
10946        let stop_index = self
10947            .parser
10948            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
10949        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
10950            self.parser.set_context_stop(&mut context, token);
10951        }
10952        Ok(self.parser.rule_node(context))
10953    }
10954
10955    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
10956        self.steps += 1;
10957        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
10958            return Err(DirectAdaptiveParseControl::Fallback(
10959                DirectAdaptiveFallback::StepLimit,
10960            ));
10961        }
10962        Ok(())
10963    }
10964
10965    fn next_transition(
10966        &mut self,
10967        state_number: usize,
10968        precedence: i32,
10969    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
10970        let state = self
10971            .atn
10972            .state(state_number)
10973            .ok_or(DirectAdaptiveParseControl::Fallback(
10974                DirectAdaptiveFallback::MissingAtn,
10975            ))?;
10976        if state.is_rule_stop() {
10977            return Err(DirectAdaptiveParseControl::Fallback(
10978                DirectAdaptiveFallback::RuleStop,
10979            ));
10980        }
10981        let transition_index =
10982            self.transition_index(state_number, state.transitions().len(), precedence)?;
10983        let transition = state.transitions().get(transition_index).ok_or(
10984            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
10985        )?;
10986        let boundary = match &transition.data() {
10987            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
10988                left_recursive_boundary(self.atn, state, *target)
10989            }
10990            _ => None,
10991        };
10992        Ok((transition, boundary))
10993    }
10994
10995    fn transition_index(
10996        &mut self,
10997        state_number: usize,
10998        transition_count: usize,
10999        precedence: i32,
11000    ) -> DirectAdaptiveParseResult<usize> {
11001        match transition_count {
11002            0 => Err(DirectAdaptiveParseControl::Fallback(
11003                DirectAdaptiveFallback::NoTransition,
11004            )),
11005            1 => Ok(0),
11006            _ => {
11007                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
11008                    return Ok(alt);
11009                }
11010                let decision = self
11011                    .decision_by_state
11012                    .get(state_number)
11013                    .and_then(|decision| *decision)
11014                    .ok_or(DirectAdaptiveParseControl::Fallback(
11015                        DirectAdaptiveFallback::UnknownDecision,
11016                    ))?;
11017                let prediction = self
11018                    .simulator
11019                    .adaptive_predict_stream_info_with_precedence(
11020                        decision,
11021                        direct_precedence(precedence),
11022                        &mut self.parser.input,
11023                    )
11024                    .map_err(|_| {
11025                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
11026                    })?;
11027                if prediction.has_semantic_context {
11028                    return Err(DirectAdaptiveParseControl::Fallback(
11029                        DirectAdaptiveFallback::SemanticContext,
11030                    ));
11031                }
11032                prediction
11033                    .alt
11034                    .checked_sub(1)
11035                    .filter(|index| *index < transition_count)
11036                    .ok_or(DirectAdaptiveParseControl::Fallback(
11037                        DirectAdaptiveFallback::InvalidAlt,
11038                    ))
11039            }
11040        }
11041    }
11042
11043    fn ll1_transition_index(
11044        &mut self,
11045        state_number: usize,
11046        transition_count: usize,
11047    ) -> DirectAdaptiveParseResult<Option<usize>> {
11048        let state = self
11049            .atn
11050            .state(state_number)
11051            .ok_or(DirectAdaptiveParseControl::Fallback(
11052                DirectAdaptiveFallback::MissingAtn,
11053            ))?;
11054        if state.precedence_rule_decision() {
11055            return Ok(None);
11056        }
11057        let Some(rule_stop) = state
11058            .rule_index()
11059            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
11060        else {
11061            return Ok(None);
11062        };
11063        let symbol = self.parser.input.la_token(1);
11064        let entry = self
11065            .parser
11066            .cached_decision_lookahead(self.atn, state, rule_stop);
11067        Ok(
11068            ll1_greedy_alt(&entry, symbol, state.non_greedy())
11069                .filter(|alt| *alt < transition_count),
11070        )
11071    }
11072
11073    fn consume_transition(
11074        &mut self,
11075        transition: ParserTransition<'_>,
11076    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
11077        let symbol = self.parser.input.la_token(1);
11078        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
11079            return Err(DirectAdaptiveParseControl::Fallback(
11080                DirectAdaptiveFallback::TokenMismatch,
11081            ));
11082        }
11083        let token = self
11084            .parser
11085            .input
11086            .lt_id(1)
11087            .ok_or(DirectAdaptiveParseControl::Fallback(
11088                DirectAdaptiveFallback::TokenMismatch,
11089            ))?;
11090        let matched_eof = symbol == TOKEN_EOF;
11091        if !matched_eof {
11092            self.parser.consume();
11093        }
11094        let child = self
11095            .parser
11096            .build_parse_trees
11097            .then(|| self.parser.terminal_tree(token));
11098        Ok((matched_eof, child))
11099    }
11100}
11101
11102/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
11103/// transformed left-recursive rule.
11104fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
11105    if !state.precedence_rule_decision() {
11106        return None;
11107    }
11108    let target_state = atn.state(target)?;
11109    if target_state.kind() == AtnStateKind::LoopEnd {
11110        return None;
11111    }
11112    state.rule_index()
11113}
11114
11115/// Selects the first outer alternative observed for a rule path.
11116///
11117/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
11118/// outer decision. The metadata recognizer only needs this when a generated
11119/// grammar opts into that target template; otherwise the value remains `0` and
11120/// parse-tree rendering is unchanged.
11121fn next_alt_number(
11122    state: AtnState<'_>,
11123    transition_count: usize,
11124    transition_index: usize,
11125    current_alt_number: usize,
11126    track_alt_numbers: bool,
11127) -> usize {
11128    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
11129        return current_alt_number;
11130    }
11131    if matches!(
11132        state.kind(),
11133        AtnStateKind::Basic
11134            | AtnStateKind::BlockStart
11135            | AtnStateKind::PlusBlockStart
11136            | AtnStateKind::StarBlockStart
11137            | AtnStateKind::StarLoopEntry
11138    ) && !state.precedence_rule_decision()
11139    {
11140        return transition_index + 1;
11141    }
11142    current_alt_number
11143}
11144
11145/// Converts an ATN state number into the signed invoking-state slot used by
11146/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
11147fn invoking_state_number(state_number: usize) -> isize {
11148    isize::try_from(state_number).unwrap_or(isize::MAX)
11149}
11150
11151const fn packed_i32(value: u32) -> i32 {
11152    i32::from_le_bytes(value.to_le_bytes())
11153}
11154
11155fn direct_precedence(precedence: i32) -> usize {
11156    usize::try_from(precedence.max(0)).unwrap_or_default()
11157}
11158
11159fn token_input_display(token: &impl Token) -> String {
11160    format!("'{}'", token.text().unwrap_or("<EOF>"))
11161}
11162
11163fn display_input_text(text: &str) -> String {
11164    let mut out = String::new();
11165    for ch in text.chars() {
11166        match ch {
11167            '\n' => out.push_str("\\n"),
11168            '\r' => out.push_str("\\r"),
11169            '\t' => out.push_str("\\t"),
11170            other => out.push(other),
11171        }
11172    }
11173    out
11174}
11175
11176fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
11177    let (line, column) = token.map_or((0, 0), |token| (token.line(), token.column()));
11178    ParserDiagnostic {
11179        line,
11180        column,
11181        message,
11182    }
11183}
11184
11185/// Emits parser diagnostics for the selected recovered parse path.
11186#[allow(clippy::print_stderr)]
11187fn report_parser_diagnostics<'a>(diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>) {
11188    for diagnostic in diagnostics {
11189        eprintln!(
11190            "line {}:{} {}",
11191            diagnostic.line, diagnostic.column, diagnostic.message
11192        );
11193    }
11194}
11195
11196/// Emits generated parser diagnostics and lexer diagnostics in the same
11197/// source-position order as ANTLR's lazy token stream reports them.
11198#[allow(clippy::print_stderr)]
11199fn report_generated_diagnostics(
11200    parser_diagnostics: &[ParserDiagnostic],
11201    token_errors: &[TokenSourceError],
11202) {
11203    // Parser diagnostics keep their event order: Java's console and
11204    // DiagnosticErrorListener print reports as prediction produces them, so
11205    // `reportAttemptingFullContext` precedes `reportContextSensitivity` even
11206    // though the latter's position is earlier. Buffered token-source errors
11207    // interleave by source position — ANTLR's lazy token stream surfaces a
11208    // lexer error when the parser first fetches that token — and win ties.
11209    let mut token_iter = token_errors.iter().peekable();
11210    for diagnostic in parser_diagnostics {
11211        while let Some(error) = token_iter.peek() {
11212            if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
11213                eprintln!("line {}:{} {}", error.line, error.column, error.message);
11214                token_iter.next();
11215            } else {
11216                break;
11217            }
11218        }
11219        eprintln!(
11220            "line {}:{} {}",
11221            diagnostic.line, diagnostic.column, diagnostic.message
11222        );
11223    }
11224    for error in token_iter {
11225        eprintln!("line {}:{} {}", error.line, error.column, error.message);
11226    }
11227}
11228
11229/// Emits buffered token-source diagnostics after parser diagnostics that were
11230/// discovered while speculatively reading the same token stream.
11231#[allow(clippy::print_stderr)]
11232fn report_token_source_errors(errors: &[TokenSourceError]) {
11233    for error in errors {
11234        eprintln!("line {}:{} {}", error.line, error.column, error.message);
11235    }
11236}
11237
11238fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
11239    let items = symbols
11240        .iter()
11241        .map(|symbol| expected_symbol_display(*symbol, vocabulary))
11242        .collect::<Vec<_>>();
11243    if let [single] = items.as_slice() {
11244        return single.clone();
11245    }
11246    format!("{{{}}}", items.join(", "))
11247}
11248
11249fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
11250    if symbol == TOKEN_EOF {
11251        return "<EOF>".to_owned();
11252    }
11253    vocabulary.display_name(symbol)
11254}
11255
11256fn caller_follow_token_info_for_stream<S: TokenSource>(
11257    input: &mut CommonTokenStream<S>,
11258    index: usize,
11259) -> (i32, bool, bool) {
11260    // Generated callers own statement separators; leave them available when
11261    // an interpreted child rule can either stop before or consume one.
11262    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
11263        input.fill();
11264    }
11265    let token_type = input.token_type_at_index(index);
11266    let visible_channel = input.channel();
11267    let token = input.get(index);
11268    let is_boundary = token
11269        .as_ref()
11270        .and_then(Token::text)
11271        .is_some_and(is_caller_follow_boundary_text);
11272    let is_boundary_gap = token.as_ref().is_some_and(|token| {
11273        token.channel() != visible_channel || is_caller_follow_boundary_gap_text(token.text())
11274    });
11275    (token_type, is_boundary, is_boundary_gap)
11276}
11277
11278fn is_caller_follow_boundary_text(text: &str) -> bool {
11279    text.chars().any(|ch| ch == ';' || ch == '\n')
11280        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11281}
11282
11283fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
11284    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11285}
11286
11287/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
11288/// Inline insertion in those precedence loops can synthesize a missing operand
11289/// before an operator and then block the legitimate loop-exit path.
11290fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
11291    let Some(rule_index) = state.rule_index() else {
11292        return false;
11293    };
11294    atn.rule_to_start_state()
11295        .get(rule_index)
11296        .and_then(|state_number| atn.state(state_number))
11297        .is_some_and(AtnState::left_recursive_rule)
11298}
11299
11300/// Picks the better of two `parse_atn_rule` passes (with and without the
11301/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
11302/// recovered one; among recovered outcomes the second pass is preferred
11303/// because the no-prefilter walk reaches ANTLR-style recovery inside child
11304/// rules. If both passes failed, the second pass's expected-token snapshot
11305/// is returned so the caller renders the same diagnostic ANTLR would.
11306fn select_better_top_outcome(
11307    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11308    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11309    arena: &RecognitionArena,
11310) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
11311    match (first, second) {
11312        (Ok(first), Ok(second)) => {
11313            if arena.diagnostics(first.0.diagnostics).next().is_none() {
11314                Ok(first)
11315            } else {
11316                Ok(second)
11317            }
11318        }
11319        (Ok(first), Err(_)) => Ok(first),
11320        (Err(_), Ok(second)) => Ok(second),
11321        (Err(_), Err(second_expected)) => Err(second_expected),
11322    }
11323}
11324
11325/// Chooses the outermost parse result that consumed the most input.
11326///
11327/// The recognizer intentionally keeps shorter endpoints available while walking
11328/// nested rule transitions so callers can satisfy following tokens such as
11329/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
11330fn select_best_fast_outcome(
11331    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
11332    prediction_mode: PredictionMode,
11333    caller_follow: Option<&TokenBitSet>,
11334    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
11335    arena: &RecognitionArena,
11336) -> Option<FastRecognizeOutcome> {
11337    let mut best = None;
11338    let mut best_caller_follow = None;
11339    for outcome in outcomes {
11340        if matches!(
11341            prediction_mode,
11342            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11343        ) && outcome.diagnostics.is_empty()
11344            && let Some(follow) = caller_follow
11345        {
11346            let (token_type, is_boundary, _) = token_info_at(outcome.index);
11347            if is_boundary && follow.contains(token_type) {
11348                let replace =
11349                    best_caller_follow
11350                        .as_ref()
11351                        .is_none_or(|existing: &FastRecognizeOutcome| {
11352                            (outcome.index, outcome.consumed_eof)
11353                                < (existing.index, existing.consumed_eof)
11354                        });
11355                if replace {
11356                    best_caller_follow = Some(outcome);
11357                }
11358            }
11359        }
11360        let Some(existing) = best else {
11361            best = Some(outcome);
11362            continue;
11363        };
11364        let outcome_position = (outcome.index, outcome.consumed_eof);
11365        let best_position = (existing.index, existing.consumed_eof);
11366        let better = match prediction_mode {
11367            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
11368                outcome_position,
11369                outcome.diagnostics,
11370                best_position,
11371                existing.diagnostics,
11372                arena,
11373            ),
11374            PredictionMode::Sll => outcome.index > existing.index,
11375        };
11376        best = Some(if better { outcome } else { existing });
11377    }
11378    let should_use_caller_follow =
11379        best_caller_follow
11380            .as_ref()
11381            .zip(best.as_ref())
11382            .is_some_and(|(candidate, selected)| {
11383                if !selected.diagnostics.is_empty() {
11384                    return true;
11385                }
11386                candidate.index < selected.index
11387                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
11388            });
11389    if should_use_caller_follow {
11390        best_caller_follow
11391    } else {
11392        best
11393    }
11394}
11395
11396fn select_best_outcome(
11397    outcomes: impl Iterator<Item = RecognizeOutcome>,
11398    prediction_mode: PredictionMode,
11399    arena: &RecognitionArena,
11400) -> Option<RecognizeOutcome> {
11401    let outcomes = outcomes.collect::<Vec<_>>();
11402    let prefer_first_tie = outcomes
11403        .iter()
11404        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
11405    outcomes.into_iter().reduce(|best, outcome| {
11406        let outcome_position = (outcome.index, outcome.consumed_eof);
11407        let best_position = (best.index, best.consumed_eof);
11408        let better = match prediction_mode {
11409            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
11410                outcome_is_better(
11411                    outcome_position,
11412                    outcome.diagnostics,
11413                    best_position,
11414                    best.diagnostics,
11415                    arena,
11416                ) || (!prefer_first_tie
11417                    && outcome_position == best_position
11418                    && arena.diagnostics_len(outcome.diagnostics)
11419                        == arena.diagnostics_len(best.diagnostics)
11420                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
11421                        == arena.diagnostics_recovery_rank(best.diagnostics)
11422                    && (outcome.decisions < best.decisions
11423                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
11424            }
11425            PredictionMode::Sll => {
11426                outcome_position > best_position
11427                    || (outcome_position == best_position
11428                        && !prefer_first_tie
11429                        && (outcome.decisions < best.decisions
11430                            || (outcome.decisions == best.decisions
11431                                && outcome_is_better(
11432                                    outcome_position,
11433                                    outcome.diagnostics,
11434                                    best_position,
11435                                    best.diagnostics,
11436                                    arena,
11437                                ))))
11438            }
11439        };
11440        if better {
11441            return outcome;
11442        }
11443        best
11444    })
11445}
11446
11447/// Records the serialized transition order at parser decision states.
11448///
11449/// When two clean paths consume the same input, ANTLR's adaptive prediction
11450/// chooses by alternative order. Keeping this compact trace lets the metadata
11451/// recognizer distinguish greedy and non-greedy optional blocks without a full
11452/// prediction simulator.
11453fn transition_decision(
11454    atn: &Atn,
11455    state: AtnState<'_>,
11456    transition_count: usize,
11457    transition_index: usize,
11458    predicates: &[(usize, usize, ParserPredicate)],
11459) -> Option<usize> {
11460    if transition_count <= 1
11461        || state.precedence_rule_decision()
11462        || decision_reaches_unsupported_predicate(atn, state, predicates)
11463    {
11464        return None;
11465    }
11466    Some(transition_index)
11467}
11468
11469/// Reports whether a state should reset the active no-viable decision start.
11470///
11471/// Loop entry/back states are continuations of the surrounding adaptive
11472/// prediction; resetting at those states would turn LL-star failures back into
11473/// ordinary mismatches.
11474fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
11475    transition_count > 1
11476        && !matches!(
11477            state.kind(),
11478            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
11479        )
11480}
11481
11482/// Marks a farthest expected-token set as no-viable when multiple alternatives
11483/// failed after the active decision had already consumed input.
11484fn record_no_viable_if_ambiguous(
11485    expected: &mut ExpectedTokens,
11486    decision_start_index: Option<usize>,
11487    index: usize,
11488) {
11489    if expected.index == Some(index) && expected.symbols.len() > 1 {
11490        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11491            expected.record_no_viable(decision_start, index);
11492        }
11493    }
11494}
11495
11496/// Records a no-viable decision caused by a failed semantic predicate before
11497/// any consuming transition can contribute an expected-token set.
11498const fn record_predicate_no_viable(
11499    expected: &mut ExpectedTokens,
11500    decision_start_index: Option<usize>,
11501    index: usize,
11502) {
11503    if let Some(decision_start) = decision_start_index {
11504        expected.record_no_viable(decision_start, index);
11505    }
11506}
11507
11508/// Returns the active decision start only when the error is past that start.
11509const fn no_viable_decision_start(
11510    decision_start_index: Option<usize>,
11511    index: usize,
11512) -> Option<usize> {
11513    match decision_start_index {
11514        Some(start) if index > start => Some(start),
11515        _ => None,
11516    }
11517}
11518
11519/// Restores expected-token bookkeeping when a child rule found a clean
11520/// consuming path; failures in longer child alternatives should not pollute the
11521/// caller's final expectation set.
11522fn restore_expected(
11523    children: &[RecognizeOutcome],
11524    child_start_index: usize,
11525    expected: &mut ExpectedTokens,
11526    snapshot: ExpectedTokens,
11527    preserve_child_expected: bool,
11528) {
11529    if preserve_child_expected {
11530        return;
11531    }
11532    if children
11533        .iter()
11534        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
11535    {
11536        *expected = snapshot;
11537    }
11538}
11539
11540/// Reports whether a decision can reach a predicate the generator did not
11541/// translate. Static alternative order is unsafe for those context predicates.
11542fn decision_reaches_unsupported_predicate(
11543    atn: &Atn,
11544    state: AtnState<'_>,
11545    predicates: &[(usize, usize, ParserPredicate)],
11546) -> bool {
11547    state.transitions().iter().any(|transition| {
11548        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
11549    })
11550}
11551
11552/// Walks epsilon-like edges from one transition to find unsupported predicates.
11553fn transition_reaches_unsupported_predicate(
11554    atn: &Atn,
11555    transition: ParserTransition<'_>,
11556    predicates: &[(usize, usize, ParserPredicate)],
11557    visited: &mut BTreeSet<usize>,
11558) -> bool {
11559    match &transition.data() {
11560        Transition::Predicate {
11561            rule_index,
11562            pred_index,
11563            ..
11564        } => !predicates
11565            .iter()
11566            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
11567        Transition::Epsilon { target }
11568        | Transition::Action { target, .. }
11569        | Transition::Rule { target, .. } => {
11570            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
11571        }
11572        Transition::Precedence { .. }
11573        | Transition::Atom { .. }
11574        | Transition::Range { .. }
11575        | Transition::Set { .. }
11576        | Transition::NotSet { .. }
11577        | Transition::Wildcard { .. } => false,
11578    }
11579}
11580
11581/// Finds an unsupported predicate reachable before a consuming transition.
11582fn state_reaches_unsupported_predicate(
11583    atn: &Atn,
11584    state_number: usize,
11585    predicates: &[(usize, usize, ParserPredicate)],
11586    visited: &mut BTreeSet<usize>,
11587) -> bool {
11588    if !visited.insert(state_number) {
11589        return false;
11590    }
11591    let Some(state) = atn.state(state_number) else {
11592        return false;
11593    };
11594    state.transitions().iter().any(|transition| {
11595        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
11596    })
11597}
11598
11599/// Adds a decision step to the front of an already-recognized suffix path.
11600fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
11601    if let Some(decision) = decision {
11602        outcome.decisions.insert(0, decision);
11603    }
11604}
11605
11606fn outcome_is_better(
11607    outcome_position: (usize, bool),
11608    outcome_diagnostics: DiagnosticSeqId,
11609    best_position: (usize, bool),
11610    best_diagnostics: DiagnosticSeqId,
11611    arena: &RecognitionArena,
11612) -> bool {
11613    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
11614    let best_len = arena.diagnostics_len(best_diagnostics);
11615    outcome_position > best_position
11616        || (outcome_position == best_position
11617            && (outcome_len < best_len
11618                || (outcome_len == best_len
11619                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
11620                        < arena.diagnostics_recovery_rank(best_diagnostics))))
11621}
11622
11623fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
11624    if outcomes
11625        .iter()
11626        .any(|outcome| outcome.diagnostics.is_empty())
11627    {
11628        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11629    }
11630}
11631
11632fn discard_recovered_outcomes_if_clean_path_exists(
11633    outcomes: &mut Vec<RecognizeOutcome>,
11634    arena: &RecognitionArena,
11635) {
11636    if outcomes
11637        .iter()
11638        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
11639    {
11640        return;
11641    }
11642    if outcomes
11643        .iter()
11644        .any(|outcome| outcome.diagnostics.is_empty())
11645    {
11646        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11647    }
11648}
11649
11650/// Reports whether a recovered outcome came from an explicit predicate
11651/// fail-option and therefore should compete with shorter clean loop exits.
11652fn outcome_has_rule_failure_diagnostic(
11653    outcome: &RecognizeOutcome,
11654    arena: &RecognitionArena,
11655) -> bool {
11656    arena
11657        .diagnostics(outcome.diagnostics)
11658        .any(|diagnostic| diagnostic.message.starts_with("rule "))
11659}
11660
11661/// Removes equivalent endpoints before memoizing a state result while
11662/// preserving ATN transition-discovery order.
11663///
11664/// Outcomes are compared on observable recognition state — the input index,
11665/// EOF consumption, and diagnostics — without descending into the parse-tree
11666/// fragment carried by `nodes`. Two paths reaching the same point with
11667/// different node trees would otherwise prevent memoization from collapsing
11668/// equivalent suffixes and explode the speculative-path cache.
11669///
11670/// The first occurrence per recognition key wins, which matches ANTLR's
11671/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
11672/// transitions before loop-exit, so the first-discovered outcome carries the
11673/// greedy parse-tree fragment.
11674fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
11675    if outcomes.len() < 2 {
11676        return;
11677    }
11678    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
11679    outcomes.retain(|outcome| {
11680        seen.insert((
11681            outcome.index,
11682            outcome.consumed_eof,
11683            arena.diagnostics_len(outcome.diagnostics),
11684            arena.diagnostics_recovery_rank(outcome.diagnostics),
11685        ))
11686    });
11687}
11688
11689const FAST_OUTCOME_INLINE_KEYS: usize = 8;
11690const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
11691const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
11692const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
11693
11694#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11695enum FastOutcomeDedupStrategy {
11696    Inline,
11697    Dense,
11698    Sparse,
11699}
11700
11701impl FastOutcomeDedupScratch {
11702    fn prepare_dense(&mut self, word_count: usize) {
11703        while let Some(word_index) = self.touched_dense_words.pop() {
11704            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
11705        }
11706        if self.dense_words.len() < word_count {
11707            self.dense_words.resize(word_count, 0);
11708        }
11709    }
11710}
11711
11712fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
11713    let first_index = outcomes.first()?.index;
11714    let (min_index, max_index) = outcomes[1..].iter().fold(
11715        (first_index, first_index),
11716        |(min_index, max_index), outcome| {
11717            (min_index.min(outcome.index), max_index.max(outcome.index))
11718        },
11719    );
11720    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
11721    let bit_count = index_span.checked_mul(2)?;
11722    let word_count =
11723        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
11724    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
11725    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
11726    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
11727        .then_some((min_index, word_count))
11728}
11729
11730#[cfg(feature = "perf-counters")]
11731fn record_clean_fast_outcome_dedup(
11732    strategy: FastOutcomeDedupStrategy,
11733    input_len: usize,
11734    output_len: usize,
11735    dense_words: usize,
11736) {
11737    let counter = match strategy {
11738        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
11739        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
11740        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
11741    };
11742    perf_counters::inc(
11743        &perf_counters::OUTCOME_DEDUPE_INPUTS,
11744        u64::try_from(input_len).unwrap_or(u64::MAX),
11745    );
11746    perf_counters::inc(
11747        &perf_counters::OUTCOME_DEDUPE_REMOVED,
11748        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
11749    );
11750    perf_counters::inc(counter, 1);
11751    perf_counters::inc(
11752        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
11753        u64::try_from(dense_words).unwrap_or(u64::MAX),
11754    );
11755}
11756
11757/// Removes duplicate clean endpoints while preserving transition-discovery
11758/// order. Tiny lists stay on the stack; larger compact ranges use a direct
11759/// bitmap, and only wide sparse ranges pay for hashing.
11760fn dedupe_clean_fast_outcomes(
11761    outcomes: &mut Vec<FastRecognizeOutcome>,
11762    scratch: &mut FastOutcomeDedupScratch,
11763) -> FastOutcomeDedupStrategy {
11764    #[cfg(feature = "perf-counters")]
11765    let input_len = outcomes.len();
11766    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
11767        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
11768        let mut inline_len = 0_usize;
11769        outcomes.retain(|outcome| {
11770            let key = (outcome.index, outcome.consumed_eof);
11771            if inline_keys[..inline_len].contains(&key) {
11772                return false;
11773            }
11774            inline_keys[inline_len] = key;
11775            inline_len += 1;
11776            true
11777        });
11778        #[cfg(feature = "perf-counters")]
11779        record_clean_fast_outcome_dedup(
11780            FastOutcomeDedupStrategy::Inline,
11781            input_len,
11782            outcomes.len(),
11783            0,
11784        );
11785        return FastOutcomeDedupStrategy::Inline;
11786    }
11787
11788    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
11789        scratch.prepare_dense(word_count);
11790        outcomes.retain(|outcome| {
11791            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
11792            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
11793            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
11794            let word = &mut scratch.dense_words[word_index];
11795            if *word & bit != 0 {
11796                return false;
11797            }
11798            if *word == 0 {
11799                scratch
11800                    .touched_dense_words
11801                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
11802            }
11803            *word |= bit;
11804            true
11805        });
11806        #[cfg(feature = "perf-counters")]
11807        record_clean_fast_outcome_dedup(
11808            FastOutcomeDedupStrategy::Dense,
11809            input_len,
11810            outcomes.len(),
11811            word_count,
11812        );
11813        return FastOutcomeDedupStrategy::Dense;
11814    }
11815
11816    scratch.sparse_keys.clear();
11817    scratch.sparse_keys.reserve(outcomes.len());
11818    outcomes.retain(|outcome| {
11819        scratch
11820            .sparse_keys
11821            .insert((outcome.index, outcome.consumed_eof))
11822    });
11823    #[cfg(feature = "perf-counters")]
11824    record_clean_fast_outcome_dedup(
11825        FastOutcomeDedupStrategy::Sparse,
11826        input_len,
11827        outcomes.len(),
11828        0,
11829    );
11830    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
11831        scratch.sparse_keys = FxHashSet::default();
11832    }
11833    FastOutcomeDedupStrategy::Sparse
11834}
11835
11836/// Sorts and removes equivalent endpoints, including action traces and the
11837/// arena-backed node sequence's structural contents.
11838fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
11839    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
11840    outcomes
11841        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
11842}
11843
11844fn compare_recognize_outcomes(
11845    left: &RecognizeOutcome,
11846    right: &RecognizeOutcome,
11847    arena: &RecognitionArena,
11848) -> Ordering {
11849    left.index
11850        .cmp(&right.index)
11851        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
11852        .then_with(|| left.alt_number.cmp(&right.alt_number))
11853        .then_with(|| left.member_values.cmp(&right.member_values))
11854        .then_with(|| left.return_values.cmp(&right.return_values))
11855        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
11856        .then_with(|| left.decisions.cmp(&right.decisions))
11857        .then_with(|| left.actions.cmp(&right.actions))
11858        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
11859}
11860
11861impl<S, H> Recognizer for BaseParser<S, H>
11862where
11863    S: TokenSource,
11864    H: SemanticHooks,
11865{
11866    fn data(&self) -> &RecognizerData {
11867        &self.data
11868    }
11869
11870    fn data_mut(&mut self) -> &mut RecognizerData {
11871        &mut self.data
11872    }
11873}
11874
11875impl<S, H> Parser for BaseParser<S, H>
11876where
11877    S: TokenSource,
11878    H: SemanticHooks,
11879{
11880    fn build_parse_trees(&self) -> bool {
11881        self.build_parse_trees
11882    }
11883
11884    fn set_build_parse_trees(&mut self, build: bool) {
11885        self.build_parse_trees = build;
11886    }
11887
11888    fn number_of_syntax_errors(&self) -> usize {
11889        Self::number_of_syntax_errors(self)
11890    }
11891
11892    fn report_diagnostic_errors(&self) -> bool {
11893        self.report_diagnostic_errors
11894    }
11895
11896    fn set_report_diagnostic_errors(&mut self, report: bool) {
11897        self.report_diagnostic_errors = report;
11898    }
11899
11900    fn prediction_mode(&self) -> PredictionMode {
11901        self.prediction_mode
11902    }
11903
11904    fn set_prediction_mode(&mut self, mode: PredictionMode) {
11905        self.prediction_mode = mode;
11906    }
11907}
11908
11909#[cfg(test)]
11910mod tests {
11911    use super::*;
11912    use crate::atn::parser::{
11913        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
11914    };
11915    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
11916    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
11917    use crate::token_stream::CommonTokenStream;
11918    use crate::tree::{NodeKind, ParseTreeStats};
11919    use crate::vocabulary::Vocabulary;
11920    use std::mem::size_of;
11921
11922    #[test]
11923    fn fx_hasher_write_matches_typed_methods_for_full_words() {
11924        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
11925        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
11926        // fields) must hash the same as the typed methods, otherwise an
11927        // `FxHashMap` keyed on such a type silently disagrees with itself
11928        // depending on which entry point the caller used. Verify the
11929        // little-endian word equivalence this PR established.
11930        let value: u64 = 0x0102_0304_0506_0708;
11931        let mut typed = FxHasher::default();
11932        typed.write_u64(value);
11933        let mut bytewise = FxHasher::default();
11934        bytewise.write(&value.to_le_bytes());
11935        assert_eq!(typed.finish(), bytewise.finish());
11936    }
11937
11938    #[derive(Clone, Debug)]
11939    struct TestToken {
11940        spec: TokenSpec,
11941        id: TokenId,
11942        source_name: String,
11943    }
11944
11945    impl TestToken {
11946        fn new(token_type: i32) -> Self {
11947            Self {
11948                spec: TokenSpec::explicit(token_type, ""),
11949                id: TokenId::try_from(0).expect("zero token ID"),
11950                source_name: String::new(),
11951            }
11952        }
11953
11954        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
11955            Self {
11956                spec: TokenSpec::eof(index, index, line, column),
11957                id: TokenId::try_from(0).expect("zero token ID"),
11958                source_name: source_name.to_owned(),
11959            }
11960        }
11961
11962        fn with_text(mut self, text: impl Into<String>) -> Self {
11963            self.spec.text = Some(text.into());
11964            self
11965        }
11966
11967        const fn with_channel(mut self, channel: i32) -> Self {
11968            self.spec.channel = channel;
11969            self
11970        }
11971
11972        const fn with_span(mut self, start: usize, stop: usize) -> Self {
11973            self.spec.start = start;
11974            self.spec.stop = stop;
11975            self.spec.start_byte = start;
11976            self.spec.stop_byte = match stop.checked_add(1) {
11977                Some(end) if end >= start => end,
11978                Some(_) | None => start,
11979            };
11980            self
11981        }
11982
11983        const fn with_position(mut self, line: usize, column: usize) -> Self {
11984            self.spec.line = line;
11985            self.spec.column = column;
11986            self
11987        }
11988
11989        fn set_token_index(&mut self, index: isize) {
11990            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
11991        }
11992    }
11993
11994    impl Token for TestToken {
11995        fn token_id(&self) -> TokenId {
11996            self.id
11997        }
11998
11999        fn token_type(&self) -> i32 {
12000            self.spec.token_type
12001        }
12002
12003        fn channel(&self) -> i32 {
12004            self.spec.channel
12005        }
12006
12007        fn start(&self) -> usize {
12008            self.spec.start
12009        }
12010
12011        fn stop(&self) -> usize {
12012            self.spec.stop
12013        }
12014
12015        fn line(&self) -> usize {
12016            self.spec.line
12017        }
12018
12019        fn column(&self) -> usize {
12020            self.spec.column
12021        }
12022
12023        fn text(&self) -> Option<&str> {
12024            self.spec.text.as_deref()
12025        }
12026
12027        fn source_name(&self) -> &str {
12028            &self.source_name
12029        }
12030
12031        fn start_byte(&self) -> usize {
12032            self.spec.start_byte
12033        }
12034
12035        fn stop_byte(&self) -> usize {
12036            self.spec.stop_byte
12037        }
12038    }
12039
12040    #[derive(Debug)]
12041    struct Source {
12042        tokens: Vec<TestToken>,
12043        index: usize,
12044    }
12045
12046    impl TokenSource for Source {
12047        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12048            let token = self
12049                .tokens
12050                .get(self.index)
12051                .cloned()
12052                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
12053            self.index += 1;
12054            sink.push(token.spec)
12055        }
12056
12057        fn line(&self) -> usize {
12058            1
12059        }
12060
12061        fn column(&self) -> usize {
12062            self.index
12063        }
12064
12065        fn source_name(&self) -> &'static str {
12066            "parser-test"
12067        }
12068    }
12069
12070    fn mini_parser_data() -> RecognizerData {
12071        RecognizerData::new(
12072            "Mini.g4",
12073            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12074        )
12075        .with_rule_names(["s"])
12076    }
12077
12078    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
12079        let data = mini_parser_data();
12080        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
12081    }
12082
12083    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
12084    where
12085        H: SemanticHooks,
12086    {
12087        BaseParser::with_semantic_hooks(
12088            CommonTokenStream::new(Source { tokens, index: 0 }),
12089            mini_parser_data(),
12090            hooks,
12091        )
12092    }
12093
12094    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
12095        builder.finish().expect("valid packed parser ATN")
12096    }
12097
12098    fn ordinary_star_loop_atn() -> Atn {
12099        let mut atn = ParserAtnBuilder::new(2);
12100        for (state_number, kind, rule_index) in [
12101            (0, AtnStateKind::RuleStart, 0),
12102            (1, AtnStateKind::StarLoopEntry, 0),
12103            (2, AtnStateKind::Basic, 0),
12104            (3, AtnStateKind::StarLoopBack, 0),
12105            (4, AtnStateKind::LoopEnd, 0),
12106            (5, AtnStateKind::Basic, 0),
12107            (6, AtnStateKind::RuleStop, 0),
12108            (7, AtnStateKind::RuleStart, 1),
12109            (8, AtnStateKind::Basic, 1),
12110            (9, AtnStateKind::RuleStop, 1),
12111        ] {
12112            assert_eq!(
12113                atn.add_state(kind, Some(rule_index))
12114                    .expect("state")
12115                    .index(),
12116                state_number
12117            );
12118        }
12119        atn.set_rule_to_start_state(vec![0, 7])
12120            .expect("rule start states");
12121        atn.set_rule_to_stop_state(vec![6, 9])
12122            .expect("rule stop states");
12123        atn.add_decision_state(1).expect("decision state");
12124        atn.set_loop_back_state(4, 3).expect("loop back state");
12125        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12126            .expect("transition");
12127        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12128            .expect("transition");
12129        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12130            .expect("transition");
12131        atn.add_transition(
12132            2,
12133            ParserTransitionSpec::Rule {
12134                target: 7,
12135                rule_index: 1,
12136                follow_state: 3,
12137                precedence: 0,
12138            },
12139        )
12140        .expect("transition");
12141        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12142            .expect("transition");
12143        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12144            .expect("transition");
12145        atn.add_transition(
12146            5,
12147            ParserTransitionSpec::Atom {
12148                target: 6,
12149                label: TOKEN_EOF,
12150            },
12151        )
12152        .expect("transition");
12153        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12154            .expect("transition");
12155        atn.add_transition(
12156            8,
12157            ParserTransitionSpec::Atom {
12158                target: 9,
12159                label: 1,
12160            },
12161        )
12162        .expect("transition");
12163        finish_atn(atn)
12164    }
12165
12166    /// ATN for `s : (X | X X)* EOF`.
12167    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12168        let mut atn = ParserAtnBuilder::new(1);
12169        for (state_number, kind) in [
12170            (0, AtnStateKind::RuleStart),
12171            (1, AtnStateKind::StarLoopEntry),
12172            (2, AtnStateKind::StarBlockStart),
12173            (3, AtnStateKind::Basic),
12174            (4, AtnStateKind::BlockEnd),
12175            (5, AtnStateKind::StarLoopBack),
12176            (6, AtnStateKind::LoopEnd),
12177            (7, AtnStateKind::Basic),
12178            (8, AtnStateKind::RuleStop),
12179        ] {
12180            assert_eq!(
12181                atn.add_state(kind, Some(0)).expect("state").index(),
12182                state_number
12183            );
12184        }
12185        atn.set_rule_to_start_state(vec![0])
12186            .expect("rule start states");
12187        atn.set_rule_to_stop_state(vec![8])
12188            .expect("rule stop states");
12189        atn.set_end_state(2, 4).expect("block end state");
12190        atn.set_loop_back_state(6, 5).expect("loop back state");
12191        atn.add_decision_state(1).expect("decision state");
12192        atn.add_decision_state(2).expect("decision state");
12193        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12194            .expect("transition");
12195        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12196            .expect("transition");
12197        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
12198            .expect("transition");
12199        atn.add_transition(
12200            2,
12201            ParserTransitionSpec::Atom {
12202                target: 4,
12203                label: 1,
12204            },
12205        )
12206        .expect("transition");
12207        atn.add_transition(
12208            2,
12209            ParserTransitionSpec::Atom {
12210                target: 3,
12211                label: 1,
12212            },
12213        )
12214        .expect("transition");
12215        atn.add_transition(
12216            3,
12217            ParserTransitionSpec::Atom {
12218                target: 4,
12219                label: 1,
12220            },
12221        )
12222        .expect("transition");
12223        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12224            .expect("transition");
12225        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
12226            .expect("transition");
12227        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12228            .expect("transition");
12229        atn.add_transition(
12230            7,
12231            ParserTransitionSpec::Atom {
12232                target: 8,
12233                label: TOKEN_EOF,
12234            },
12235        )
12236        .expect("transition");
12237        finish_atn(atn)
12238    }
12239
12240    fn ordinary_plus_loop_atn() -> Atn {
12241        let mut atn = ParserAtnBuilder::new(2);
12242        for (state_number, kind, rule_index) in [
12243            (0, AtnStateKind::RuleStart, 0),
12244            (1, AtnStateKind::Basic, 0),
12245            (2, AtnStateKind::PlusLoopBack, 0),
12246            (3, AtnStateKind::LoopEnd, 0),
12247            (4, AtnStateKind::Basic, 0),
12248            (5, AtnStateKind::RuleStop, 0),
12249            (6, AtnStateKind::RuleStart, 1),
12250            (7, AtnStateKind::Basic, 1),
12251            (8, AtnStateKind::RuleStop, 1),
12252        ] {
12253            assert_eq!(
12254                atn.add_state(kind, Some(rule_index))
12255                    .expect("state")
12256                    .index(),
12257                state_number
12258            );
12259        }
12260        atn.set_rule_to_start_state(vec![0, 6])
12261            .expect("rule start states");
12262        atn.set_rule_to_stop_state(vec![5, 8])
12263            .expect("rule stop states");
12264        atn.add_decision_state(2).expect("decision state");
12265        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12266            .expect("transition");
12267        atn.add_transition(
12268            1,
12269            ParserTransitionSpec::Rule {
12270                target: 6,
12271                rule_index: 1,
12272                follow_state: 2,
12273                precedence: 0,
12274            },
12275        )
12276        .expect("transition");
12277        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
12278            .expect("transition");
12279        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12280            .expect("transition");
12281        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
12282            .expect("transition");
12283        atn.add_transition(
12284            4,
12285            ParserTransitionSpec::Atom {
12286                target: 5,
12287                label: TOKEN_EOF,
12288            },
12289        )
12290        .expect("transition");
12291        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12292            .expect("transition");
12293        atn.add_transition(
12294            7,
12295            ParserTransitionSpec::Atom {
12296                target: 8,
12297                label: 1,
12298            },
12299        )
12300        .expect("transition");
12301        finish_atn(atn)
12302    }
12303
12304    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
12305        let mut tokens = (0..count)
12306            .map(|_| TestToken::new(1).with_text("x"))
12307            .collect::<Vec<_>>();
12308        tokens.push(TestToken::eof("parser-test", count, 1, count));
12309        tokens
12310    }
12311
12312    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
12313        let mut atn = ParserAtnBuilder::new(2);
12314        assert_eq!(
12315            atn.add_state(AtnStateKind::RuleStart, Some(0))
12316                .expect("state")
12317                .index(),
12318            0
12319        );
12320        assert_eq!(
12321            atn.add_state(AtnStateKind::Basic, Some(0))
12322                .expect("state")
12323                .index(),
12324            1
12325        );
12326        assert_eq!(
12327            atn.add_state(AtnStateKind::Basic, Some(0))
12328                .expect("state")
12329                .index(),
12330            2
12331        );
12332        assert_eq!(
12333            atn.add_state(AtnStateKind::RuleStart, Some(1))
12334                .expect("state")
12335                .index(),
12336            3
12337        );
12338        atn.set_left_recursive_rule(3)
12339            .expect("left-recursive rule start");
12340        assert_eq!(
12341            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
12342                .expect("state")
12343                .index(),
12344            4
12345        );
12346        atn.set_precedence_rule_decision(4)
12347            .expect("precedence decision");
12348        assert_eq!(
12349            atn.add_state(AtnStateKind::Basic, Some(1))
12350                .expect("state")
12351                .index(),
12352            5
12353        );
12354        assert_eq!(
12355            atn.add_state(AtnStateKind::Basic, Some(1))
12356                .expect("state")
12357                .index(),
12358            6
12359        );
12360        assert_eq!(
12361            atn.add_state(AtnStateKind::LoopEnd, Some(1))
12362                .expect("state")
12363                .index(),
12364            7
12365        );
12366        assert_eq!(
12367            atn.add_state(AtnStateKind::RuleStop, Some(1))
12368                .expect("state")
12369                .index(),
12370            8
12371        );
12372        assert_eq!(
12373            atn.add_state(AtnStateKind::RuleStop, Some(0))
12374                .expect("state")
12375                .index(),
12376            9
12377        );
12378        atn.set_rule_to_start_state(vec![0, 3])
12379            .expect("rule start states");
12380        atn.set_rule_to_stop_state(vec![9, 8])
12381            .expect("rule stop states");
12382        atn.add_transition(
12383            1,
12384            ParserTransitionSpec::Rule {
12385                target: 3,
12386                rule_index: 1,
12387                follow_state: 2,
12388                precedence: 0,
12389            },
12390        )
12391        .expect("transition");
12392        atn.add_transition(
12393            2,
12394            ParserTransitionSpec::Atom {
12395                target: 9,
12396                label: caller_symbol,
12397            },
12398        )
12399        .expect("transition");
12400        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12401            .expect("transition");
12402        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
12403            .expect("transition");
12404        atn.add_transition(
12405            5,
12406            ParserTransitionSpec::Precedence {
12407                target: 6,
12408                precedence: 1,
12409            },
12410        )
12411        .expect("transition");
12412        atn.add_transition(
12413            6,
12414            ParserTransitionSpec::Atom {
12415                target: 4,
12416                label: 1,
12417            },
12418        )
12419        .expect("transition");
12420        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12421            .expect("transition");
12422        finish_atn(atn)
12423    }
12424
12425    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
12426        let mut parser = mini_parser(vec![
12427            TestToken::new(symbol).with_text("lookahead"),
12428            TestToken::eof("parser-test", 1, 1, 1),
12429        ]);
12430        parser.rule_context_stack = vec![
12431            RuleContextFrame {
12432                rule_index: 0,
12433                invoking_state: -1,
12434            },
12435            RuleContextFrame {
12436                rule_index: 1,
12437                invoking_state: 1,
12438            },
12439        ];
12440        parser
12441    }
12442
12443    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
12444        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
12445        //   prec 2: token 1, token 1  (shift `>>`)
12446        //   prec 1: token 1           (relational `>`)
12447        let mut atn = ParserAtnBuilder::new(1);
12448        for (state, kind, rule) in [
12449            (0, AtnStateKind::RuleStart, 0),
12450            (1, AtnStateKind::StarLoopEntry, 0),
12451            (2, AtnStateKind::Basic, 0), // ops hub
12452            (3, AtnStateKind::Basic, 0), // shift prec
12453            (4, AtnStateKind::Basic, 0), // shift first >
12454            (5, AtnStateKind::Basic, 0), // shift second >
12455            (6, AtnStateKind::Basic, 0), // rel prec
12456            (7, AtnStateKind::Basic, 0), // rel >
12457            (8, AtnStateKind::LoopEnd, 0),
12458            (9, AtnStateKind::RuleStop, 0),
12459        ] {
12460            assert_eq!(
12461                atn.add_state(kind, Some(rule)).expect("state").index(),
12462                state
12463            );
12464            if state == 0 {
12465                atn.set_left_recursive_rule(state)
12466                    .expect("left-recursive rule start");
12467            } else if state == 1 {
12468                atn.set_precedence_rule_decision(state)
12469                    .expect("precedence decision");
12470            }
12471        }
12472        atn.set_rule_to_start_state(vec![0])
12473            .expect("rule start states");
12474        atn.set_rule_to_stop_state(vec![9])
12475            .expect("rule stop states");
12476        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12477            .expect("ops");
12478        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12479            .expect("exit");
12480        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12481            .expect("to shift");
12482        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12483            .expect("to rel");
12484        atn.add_transition(
12485            3,
12486            ParserTransitionSpec::Precedence {
12487                target: 4,
12488                precedence: 2,
12489            },
12490        )
12491        .expect("shift prec");
12492        atn.add_transition(
12493            4,
12494            ParserTransitionSpec::Atom {
12495                target: 5,
12496                label: 1,
12497            },
12498        )
12499        .expect("shift first >");
12500        atn.add_transition(
12501            5,
12502            ParserTransitionSpec::Atom {
12503                target: 1,
12504                label: 1,
12505            },
12506        )
12507        .expect("shift second >");
12508        atn.add_transition(
12509            6,
12510            ParserTransitionSpec::Precedence {
12511                target: 7,
12512                precedence: 1,
12513            },
12514        )
12515        .expect("rel prec");
12516        atn.add_transition(
12517            7,
12518            ParserTransitionSpec::Atom {
12519                target: 1,
12520                label: 1,
12521            },
12522        )
12523        .expect("rel >");
12524        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12525            .expect("loop end");
12526        finish_atn(atn)
12527    }
12528
12529    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
12530        let mut atn = ParserAtnBuilder::new(2);
12531        for (state, kind, rule) in [
12532            (0, AtnStateKind::RuleStart, 0),
12533            (1, AtnStateKind::StarLoopEntry, 0),
12534            (2, AtnStateKind::Basic, 0),
12535            (3, AtnStateKind::Basic, 0),
12536            (4, AtnStateKind::Basic, 0),
12537            (5, AtnStateKind::Basic, 0),
12538            (6, AtnStateKind::Basic, 0),
12539            (7, AtnStateKind::Basic, 0),
12540            (8, AtnStateKind::LoopEnd, 0),
12541            (9, AtnStateKind::RuleStop, 0),
12542            (10, AtnStateKind::RuleStart, 1),
12543            (11, AtnStateKind::Basic, 1),
12544            (12, AtnStateKind::RuleStop, 1),
12545        ] {
12546            assert_eq!(
12547                atn.add_state(kind, Some(rule)).expect("state").index(),
12548                state
12549            );
12550            if state == 0 {
12551                atn.set_left_recursive_rule(state)
12552                    .expect("left-recursive rule start");
12553            } else if state == 1 {
12554                atn.set_precedence_rule_decision(state)
12555                    .expect("precedence decision");
12556            }
12557        }
12558        atn.set_rule_to_start_state(vec![0, 10])
12559            .expect("rule start states");
12560        atn.set_rule_to_stop_state(vec![9, 12])
12561            .expect("rule stop states");
12562        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12563            .expect("ops");
12564        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12565            .expect("exit");
12566        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12567            .expect("to shift");
12568        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12569            .expect("to relational");
12570        atn.add_transition(
12571            3,
12572            ParserTransitionSpec::Precedence {
12573                target: 4,
12574                precedence: 2,
12575            },
12576        )
12577        .expect("shift precedence");
12578        atn.add_transition(
12579            4,
12580            ParserTransitionSpec::Rule {
12581                target: 10,
12582                rule_index: 1,
12583                follow_state: 5,
12584                precedence: 0,
12585            },
12586        )
12587        .expect("first shift token helper");
12588        atn.add_transition(
12589            5,
12590            ParserTransitionSpec::Atom {
12591                target: 1,
12592                label: 1,
12593            },
12594        )
12595        .expect("second shift token");
12596        atn.add_transition(
12597            6,
12598            ParserTransitionSpec::Precedence {
12599                target: 7,
12600                precedence: 1,
12601            },
12602        )
12603        .expect("relational precedence");
12604        atn.add_transition(
12605            7,
12606            ParserTransitionSpec::Atom {
12607                target: 1,
12608                label: 1,
12609            },
12610        )
12611        .expect("relational token");
12612        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12613            .expect("loop end");
12614        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
12615            .expect("helper entry");
12616        atn.add_transition(
12617            11,
12618            ParserTransitionSpec::Atom {
12619                target: 12,
12620                label: 1,
12621            },
12622        )
12623        .expect("first shift token");
12624        finish_atn(atn)
12625    }
12626
12627    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
12628        let mut atn = ParserAtnBuilder::new(1);
12629        for (state, kind) in [
12630            (0, AtnStateKind::RuleStart),
12631            (1, AtnStateKind::StarLoopEntry),
12632            (2, AtnStateKind::Basic),
12633            (3, AtnStateKind::Basic),
12634            (4, AtnStateKind::Basic),
12635            (5, AtnStateKind::Basic),
12636            (6, AtnStateKind::Basic),
12637            (7, AtnStateKind::Basic),
12638            (8, AtnStateKind::Basic),
12639            (9, AtnStateKind::LoopEnd),
12640            (10, AtnStateKind::RuleStop),
12641        ] {
12642            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
12643            if state == 0 {
12644                atn.set_left_recursive_rule(state)
12645                    .expect("left-recursive rule start");
12646            } else if state == 1 {
12647                atn.set_precedence_rule_decision(state)
12648                    .expect("precedence decision");
12649            }
12650        }
12651        atn.set_rule_to_start_state(vec![0])
12652            .expect("rule start states");
12653        atn.set_rule_to_stop_state(vec![10])
12654            .expect("rule stop states");
12655        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12656            .expect("ops");
12657        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
12658            .expect("exit");
12659        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12660            .expect("to multi-token operator");
12661        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12662            .expect("to predicate operator");
12663        atn.add_transition(
12664            3,
12665            ParserTransitionSpec::Precedence {
12666                target: 4,
12667                precedence: 2,
12668            },
12669        )
12670        .expect("multi-token precedence");
12671        atn.add_transition(
12672            4,
12673            ParserTransitionSpec::Atom {
12674                target: 5,
12675                label: 1,
12676            },
12677        )
12678        .expect("multi-token first");
12679        atn.add_transition(
12680            5,
12681            ParserTransitionSpec::Atom {
12682                target: 1,
12683                label: 1,
12684            },
12685        )
12686        .expect("multi-token second");
12687        atn.add_transition(
12688            6,
12689            ParserTransitionSpec::Precedence {
12690                target: 7,
12691                precedence: 2,
12692            },
12693        )
12694        .expect("predicate precedence");
12695        atn.add_transition(
12696            7,
12697            ParserTransitionSpec::Predicate {
12698                target: 8,
12699                rule_index: 0,
12700                pred_index: 0,
12701                context_dependent: false,
12702            },
12703        )
12704        .expect("operator predicate");
12705        atn.add_transition(
12706            8,
12707            ParserTransitionSpec::Atom {
12708                target: 1,
12709                label: 1,
12710            },
12711        )
12712        .expect("predicate single token");
12713        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
12714            .expect("loop end");
12715        finish_atn(atn)
12716    }
12717
12718    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
12719        let mut atn = ParserAtnBuilder::new(2);
12720        for (state, kind, rule) in [
12721            (0, AtnStateKind::RuleStart, 0),
12722            (1, AtnStateKind::StarLoopEntry, 0),
12723            (2, AtnStateKind::Basic, 0),
12724            (3, AtnStateKind::Basic, 0),
12725            (4, AtnStateKind::Basic, 0),
12726            (5, AtnStateKind::LoopEnd, 0),
12727            (6, AtnStateKind::RuleStop, 0),
12728            (7, AtnStateKind::RuleStart, 1),
12729            (8, AtnStateKind::RuleStop, 1),
12730            (9, AtnStateKind::Basic, 1),
12731        ] {
12732            assert_eq!(
12733                atn.add_state(kind, Some(rule)).expect("state").index(),
12734                state
12735            );
12736            if state == 0 {
12737                atn.set_left_recursive_rule(state)
12738                    .expect("left-recursive rule start");
12739            } else if state == 1 {
12740                atn.set_precedence_rule_decision(state)
12741                    .expect("precedence decision");
12742            }
12743        }
12744        atn.set_rule_to_start_state(vec![0, 7])
12745            .expect("rule start states");
12746        atn.set_rule_to_stop_state(vec![6, 8])
12747            .expect("rule stop states");
12748        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12749            .expect("transition");
12750        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
12751            .expect("transition");
12752        atn.add_transition(
12753            2,
12754            ParserTransitionSpec::Precedence {
12755                target: 3,
12756                precedence: 3,
12757            },
12758        )
12759        .expect("transition");
12760        atn.add_transition(
12761            3,
12762            ParserTransitionSpec::Rule {
12763                target: 7,
12764                rule_index: 1,
12765                follow_state: 4,
12766                precedence: 0,
12767            },
12768        )
12769        .expect("transition");
12770        atn.add_transition(
12771            4,
12772            ParserTransitionSpec::Atom {
12773                target: 1,
12774                label: 1,
12775            },
12776        )
12777        .expect("transition");
12778        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
12779            .expect("transition");
12780        atn.add_transition(
12781            7,
12782            ParserTransitionSpec::Precedence {
12783                target: 9,
12784                precedence: 1,
12785            },
12786        )
12787        .expect("transition");
12788        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
12789            .expect("transition");
12790        finish_atn(atn)
12791    }
12792
12793    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
12794        let mut atn = ParserAtnBuilder::new(2);
12795        for (state, kind) in [
12796            (0, AtnStateKind::RuleStart),
12797            (1, AtnStateKind::StarLoopEntry),
12798            (2, AtnStateKind::Basic),
12799            (3, AtnStateKind::Basic),
12800            (4, AtnStateKind::Basic),
12801            (5, AtnStateKind::LoopEnd),
12802            (6, AtnStateKind::RuleStop),
12803        ] {
12804            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
12805            if state == 0 {
12806                atn.set_left_recursive_rule(state)
12807                    .expect("left-recursive rule start");
12808            } else if state == 1 {
12809                atn.set_precedence_rule_decision(state)
12810                    .expect("precedence decision");
12811            }
12812        }
12813        atn.set_rule_to_start_state(vec![0])
12814            .expect("rule start states");
12815        atn.set_rule_to_stop_state(vec![6])
12816            .expect("rule stop states");
12817        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12818            .expect("transition");
12819        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
12820            .expect("transition");
12821        atn.add_transition(
12822            2,
12823            ParserTransitionSpec::Precedence {
12824                target: 3,
12825                precedence: 1,
12826            },
12827        )
12828        .expect("transition");
12829        atn.add_transition(
12830            3,
12831            ParserTransitionSpec::Predicate {
12832                target: 4,
12833                rule_index: 0,
12834                pred_index: 0,
12835                context_dependent: false,
12836            },
12837        )
12838        .expect("transition");
12839        atn.add_transition(
12840            4,
12841            ParserTransitionSpec::Atom {
12842                target: 1,
12843                label: 1,
12844            },
12845        )
12846        .expect("transition");
12847        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
12848            .expect("transition");
12849        finish_atn(atn)
12850    }
12851
12852    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
12853        let mut atn = ParserAtnBuilder::new(2);
12854        for (state, kind, rule) in [
12855            (0, AtnStateKind::RuleStart, 0),
12856            (1, AtnStateKind::Basic, 0),
12857            (2, AtnStateKind::Basic, 0),
12858            (3, AtnStateKind::Basic, 0),
12859            (4, AtnStateKind::RuleStop, 0),
12860            (5, AtnStateKind::RuleStart, 1),
12861            (6, AtnStateKind::StarLoopEntry, 1),
12862            (7, AtnStateKind::Basic, 1),
12863            (8, AtnStateKind::Basic, 1),
12864            (9, AtnStateKind::LoopEnd, 1),
12865            (10, AtnStateKind::RuleStop, 1),
12866            (11, AtnStateKind::RuleStart, 2),
12867            (12, AtnStateKind::RuleStop, 2),
12868        ] {
12869            assert_eq!(
12870                atn.add_state(kind, Some(rule)).expect("state").index(),
12871                state
12872            );
12873            if state == 5 {
12874                atn.set_left_recursive_rule(state)
12875                    .expect("left-recursive rule start");
12876            } else if state == 6 {
12877                atn.set_precedence_rule_decision(state)
12878                    .expect("precedence decision");
12879            }
12880        }
12881        atn.set_rule_to_start_state(vec![0, 5, 11])
12882            .expect("rule start states");
12883        atn.set_rule_to_stop_state(vec![4, 10, 12])
12884            .expect("rule stop states");
12885        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12886            .expect("transition");
12887        atn.add_transition(
12888            1,
12889            ParserTransitionSpec::Rule {
12890                target: 5,
12891                rule_index: 1,
12892                follow_state: 2,
12893                precedence: 0,
12894            },
12895        )
12896        .expect("transition");
12897        atn.add_transition(
12898            2,
12899            ParserTransitionSpec::Rule {
12900                target: 11,
12901                rule_index: 2,
12902                follow_state: 3,
12903                precedence: 0,
12904            },
12905        )
12906        .expect("transition");
12907        atn.add_transition(
12908            3,
12909            ParserTransitionSpec::Atom {
12910                target: 4,
12911                label: caller_symbol,
12912            },
12913        )
12914        .expect("transition");
12915        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12916            .expect("transition");
12917        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
12918            .expect("transition");
12919        atn.add_transition(
12920            7,
12921            ParserTransitionSpec::Precedence {
12922                target: 8,
12923                precedence: 1,
12924            },
12925        )
12926        .expect("transition");
12927        atn.add_transition(
12928            8,
12929            ParserTransitionSpec::Atom {
12930                target: 6,
12931                label: 1,
12932            },
12933        )
12934        .expect("transition");
12935        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
12936            .expect("transition");
12937        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
12938            .expect("transition");
12939        finish_atn(atn)
12940    }
12941
12942    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
12943        let mut atn = ParserAtnBuilder::new(2);
12944        for (state, kind, rule) in [
12945            (0, AtnStateKind::RuleStart, 0),
12946            (1, AtnStateKind::Basic, 0),
12947            (2, AtnStateKind::Basic, 0),
12948            (3, AtnStateKind::RuleStop, 0),
12949            (4, AtnStateKind::RuleStart, 1),
12950            (5, AtnStateKind::Basic, 1),
12951            (6, AtnStateKind::Basic, 1),
12952            (7, AtnStateKind::RuleStop, 1),
12953            (8, AtnStateKind::RuleStart, 2),
12954            (9, AtnStateKind::StarLoopEntry, 2),
12955            (10, AtnStateKind::Basic, 2),
12956            (11, AtnStateKind::Basic, 2),
12957            (12, AtnStateKind::LoopEnd, 2),
12958            (13, AtnStateKind::RuleStop, 2),
12959        ] {
12960            assert_eq!(
12961                atn.add_state(kind, Some(rule)).expect("state").index(),
12962                state
12963            );
12964            if state == 8 {
12965                atn.set_left_recursive_rule(state)
12966                    .expect("left-recursive rule start");
12967            } else if state == 9 {
12968                atn.set_precedence_rule_decision(state)
12969                    .expect("precedence decision");
12970            }
12971        }
12972        atn.set_rule_to_start_state(vec![0, 4, 8])
12973            .expect("rule start states");
12974        atn.set_rule_to_stop_state(vec![3, 7, 13])
12975            .expect("rule stop states");
12976        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12977            .expect("transition");
12978        atn.add_transition(
12979            1,
12980            ParserTransitionSpec::Rule {
12981                target: 4,
12982                rule_index: 1,
12983                follow_state: 2,
12984                precedence: 0,
12985            },
12986        )
12987        .expect("transition");
12988        atn.add_transition(
12989            2,
12990            ParserTransitionSpec::Atom {
12991                target: 3,
12992                label: caller_symbol,
12993            },
12994        )
12995        .expect("transition");
12996        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12997            .expect("transition");
12998        atn.add_transition(
12999            5,
13000            ParserTransitionSpec::Rule {
13001                target: 8,
13002                rule_index: 2,
13003                follow_state: 6,
13004                precedence: 0,
13005            },
13006        )
13007        .expect("transition");
13008        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13009            .expect("transition");
13010        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13011            .expect("transition");
13012        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
13013            .expect("transition");
13014        atn.add_transition(
13015            10,
13016            ParserTransitionSpec::Precedence {
13017                target: 11,
13018                precedence: 1,
13019            },
13020        )
13021        .expect("transition");
13022        atn.add_transition(
13023            11,
13024            ParserTransitionSpec::Atom {
13025                target: 9,
13026                label: 1,
13027            },
13028        )
13029        .expect("transition");
13030        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
13031            .expect("transition");
13032        finish_atn(atn)
13033    }
13034
13035    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
13036        let mut atn = ParserAtnBuilder::new(2);
13037        for (state, kind, rule) in [
13038            (0, AtnStateKind::RuleStart, 0),
13039            (1, AtnStateKind::Basic, 0),
13040            (2, AtnStateKind::Basic, 0),
13041            (3, AtnStateKind::RuleStop, 0),
13042            (4, AtnStateKind::RuleStart, 1),
13043            (5, AtnStateKind::StarLoopEntry, 1),
13044            (6, AtnStateKind::Basic, 1),
13045            (7, AtnStateKind::Basic, 1),
13046            (8, AtnStateKind::Basic, 1),
13047            (9, AtnStateKind::Basic, 1),
13048            (10, AtnStateKind::LoopEnd, 1),
13049            (11, AtnStateKind::RuleStop, 1),
13050        ] {
13051            assert_eq!(
13052                atn.add_state(kind, Some(rule)).expect("state").index(),
13053                state
13054            );
13055            if state == 4 {
13056                atn.set_left_recursive_rule(state)
13057                    .expect("left-recursive rule start");
13058            } else if state == 5 {
13059                atn.set_precedence_rule_decision(state)
13060                    .expect("precedence decision");
13061            }
13062        }
13063        atn.set_rule_to_start_state(vec![0, 4])
13064            .expect("rule start states");
13065        atn.set_rule_to_stop_state(vec![3, 11])
13066            .expect("rule stop states");
13067        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13068            .expect("transition");
13069        atn.add_transition(
13070            1,
13071            ParserTransitionSpec::Rule {
13072                target: 4,
13073                rule_index: 1,
13074                follow_state: 2,
13075                precedence: 0,
13076            },
13077        )
13078        .expect("transition");
13079        atn.add_transition(
13080            2,
13081            ParserTransitionSpec::Atom {
13082                target: 3,
13083                label: caller_symbol,
13084            },
13085        )
13086        .expect("transition");
13087        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13088            .expect("transition");
13089        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
13090            .expect("transition");
13091        atn.add_transition(
13092            6,
13093            ParserTransitionSpec::Precedence {
13094                target: 7,
13095                precedence: 1,
13096            },
13097        )
13098        .expect("transition");
13099        atn.add_transition(
13100            7,
13101            ParserTransitionSpec::Atom {
13102                target: 8,
13103                label: 1,
13104            },
13105        )
13106        .expect("transition");
13107        atn.add_transition(
13108            8,
13109            ParserTransitionSpec::Rule {
13110                target: 4,
13111                rule_index: 1,
13112                follow_state: 9,
13113                precedence: 2,
13114            },
13115        )
13116        .expect("transition");
13117        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13118            .expect("transition");
13119        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13120            .expect("transition");
13121        finish_atn(atn)
13122    }
13123
13124    #[test]
13125    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13126        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13127        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13128
13129        let mut overlapping = parser_inside_left_recursive_callee(1);
13130        assert_eq!(
13131            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13132            None
13133        );
13134
13135        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13136        assert_eq!(
13137            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13138            Some(true)
13139        );
13140
13141        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13142        assert_eq!(
13143            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13144            Some(false)
13145        );
13146
13147        assert_eq!(
13148            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13149            Some(true),
13150            "overlap results must not leak across ATNs"
13151        );
13152    }
13153
13154    #[test]
13155    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
13156        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
13157        let mut parser = mini_parser(vec![
13158            TestToken::new(1).with_text("operator"),
13159            TestToken::eof("parser-test", 1, 1, 1),
13160        ]);
13161        parser.rule_context_stack = vec![RuleContextFrame {
13162            rule_index: 0,
13163            invoking_state: -1,
13164        }];
13165
13166        assert_eq!(
13167            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13168            Some(true)
13169        );
13170        assert_eq!(
13171            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13172            Some(true),
13173            "cached operator lookahead must preserve the nullable prefix return path"
13174        );
13175        assert_eq!(
13176            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13177            Some(true),
13178            "the nullable child must use its rule-call precedence, not the caller precedence"
13179        );
13180    }
13181
13182    #[test]
13183    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
13184        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
13185        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
13186        // must defer so StarLoopEntry adaptive predict can exit when the second
13187        // `>` is absent (as in `a < b > c`).
13188        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
13189        let mut parser = mini_parser(vec![
13190            TestToken::new(1).with_text(">"),
13191            TestToken::new(2).with_text("id"),
13192            TestToken::eof("parser-test", 1, 1, 1),
13193        ]);
13194        parser.rule_context_stack = vec![RuleContextFrame {
13195            rule_index: 0,
13196            invoking_state: -1,
13197        }];
13198
13199        assert_eq!(
13200            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13201            Some(true),
13202            "at low precedence relational `>` is a single-token operator"
13203        );
13204        assert_eq!(
13205            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
13206            Some(true),
13207            "relational remains single-token at its own precedence"
13208        );
13209        assert_eq!(
13210            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13211            None,
13212            "at shift precedence, bare `>` must not force enter"
13213        );
13214    }
13215
13216    #[test]
13217    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
13218        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
13219        let mut parser = mini_parser(vec![
13220            TestToken::new(1).with_text(">"),
13221            TestToken::new(2).with_text("id"),
13222            TestToken::eof("parser-test", 1, 1, 1),
13223        ]);
13224        parser.rule_context_stack = vec![RuleContextFrame {
13225            rule_index: 0,
13226            invoking_state: -1,
13227        }];
13228
13229        assert_eq!(
13230            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13231            Some(true),
13232            "the direct relational alternative remains a one-token operator"
13233        );
13234        assert_eq!(
13235            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13236            None,
13237            "a token matched in the helper rule must return to the second shift token"
13238        );
13239    }
13240
13241    #[test]
13242    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
13243        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
13244        let mut parser = mini_parser(vec![
13245            TestToken::new(1).with_text(">"),
13246            TestToken::new(2).with_text("id"),
13247            TestToken::eof("parser-test", 1, 1, 1),
13248        ]);
13249        parser.rule_context_stack = vec![RuleContextFrame {
13250            rule_index: 0,
13251            invoking_state: -1,
13252        }];
13253
13254        assert_eq!(
13255            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13256            None,
13257            "a predicate-gated single-token path must not be hidden by a multi-token path"
13258        );
13259    }
13260
13261    #[test]
13262    fn left_recursive_loop_defers_predicate_guarded_operator() {
13263        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
13264        let mut parser = mini_parser_with_hooks(
13265            vec![
13266                TestToken::new(1).with_text("operator"),
13267                TestToken::eof("parser-test", 1, 1, 1),
13268            ],
13269            RejectingPredicateHooks::default(),
13270        );
13271        parser.rule_context_stack = vec![RuleContextFrame {
13272            rule_index: 0,
13273            invoking_state: -1,
13274        }];
13275
13276        assert_eq!(
13277            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13278            None,
13279            "a false predicate must be evaluated before entering the operator alternative"
13280        );
13281        assert_eq!(
13282            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13283            None,
13284            "cached predicate-dependent lookahead must keep deferring"
13285        );
13286    }
13287
13288    #[test]
13289    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
13290        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
13291        let mut parser = parser_inside_left_recursive_callee(1);
13292
13293        assert_eq!(
13294            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13295            None
13296        );
13297        assert_eq!(
13298            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13299            None,
13300            "the cached overlap must preserve the nullable child return path"
13301        );
13302    }
13303
13304    #[test]
13305    fn left_recursive_loop_defers_through_nullable_parent_return() {
13306        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
13307        let mut parser = mini_parser(vec![
13308            TestToken::new(1).with_text("lookahead"),
13309            TestToken::eof("parser-test", 1, 1, 1),
13310        ]);
13311        parser.rule_context_stack = vec![
13312            RuleContextFrame {
13313                rule_index: 0,
13314                invoking_state: -1,
13315            },
13316            RuleContextFrame {
13317                rule_index: 1,
13318                invoking_state: 1,
13319            },
13320            RuleContextFrame {
13321                rule_index: 2,
13322                invoking_state: 5,
13323            },
13324        ];
13325
13326        assert_eq!(
13327            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13328            None,
13329            "a nullable caller must unwind to its parent's consuming follow path"
13330        );
13331        assert_eq!(
13332            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13333            None,
13334            "the caller-overlap cache must not retain a false negative"
13335        );
13336    }
13337
13338    #[test]
13339    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
13340        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
13341        let mut parser = mini_parser(vec![
13342            TestToken::new(1).with_text("lookahead"),
13343            TestToken::eof("parser-test", 1, 1, 1),
13344        ]);
13345        parser.rule_context_stack = vec![
13346            RuleContextFrame {
13347                rule_index: 0,
13348                invoking_state: -1,
13349            },
13350            RuleContextFrame {
13351                rule_index: 1,
13352                invoking_state: 1,
13353            },
13354            RuleContextFrame {
13355                rule_index: 1,
13356                invoking_state: 8,
13357            },
13358        ];
13359
13360        assert_eq!(
13361            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13362            None,
13363            "a recursive operand return must preserve its parent caller context"
13364        );
13365        assert_eq!(
13366            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13367            None,
13368            "the caller-overlap cache must preserve the loop-boundary return"
13369        );
13370    }
13371
13372    fn token_then_eof_atn() -> Atn {
13373        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13374            4, 1, 2, // version, parser, max token type
13375            3, // states
13376            2, 0, // rule start
13377            1, 0, // basic
13378            7, 0, // rule stop
13379            0, // non-greedy states
13380            0, // precedence states
13381            1, // rules
13382            0, // rule 0 start
13383            0, // modes
13384            0, // sets
13385            2, // transitions
13386            0, 1, 5, 1, 0, 0, // match token 1
13387            1, 2, 5, -1, 0, 0, // match EOF
13388            0, // decisions
13389        ]))
13390        .deserialize_parser()
13391        .expect("artificial parser ATN should deserialize")
13392    }
13393
13394    fn epsilon_cycle_atn() -> Atn {
13395        let mut atn = ParserAtnBuilder::new(1);
13396        for (state_number, kind) in [
13397            (0, AtnStateKind::RuleStart),
13398            (1, AtnStateKind::Basic),
13399            (2, AtnStateKind::RuleStop),
13400        ] {
13401            assert_eq!(
13402                atn.add_state(kind, Some(0)).expect("state").index(),
13403                state_number
13404            );
13405        }
13406        atn.set_rule_to_start_state(vec![0])
13407            .expect("rule start states");
13408        atn.set_rule_to_stop_state(vec![2])
13409            .expect("rule stop states");
13410        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13411            .expect("transition");
13412        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
13413            .expect("self-cycle transition");
13414        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13415            .expect("exit transition");
13416        finish_atn(atn)
13417    }
13418
13419    fn eof_then_action_atn() -> Atn {
13420        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13421            4, 1, 1, // version, parser, max token type
13422            3, // states
13423            2, 0, // rule start
13424            1, 0, // basic
13425            7, 0, // rule stop
13426            0, // non-greedy states
13427            0, // precedence states
13428            1, // rules
13429            0, // rule 0 start
13430            0, // modes
13431            0, // sets
13432            2, // transitions
13433            0, 1, 5, -1, 0, 0, // match EOF
13434            1, 2, 6, 0, 0, 0, // parser action
13435            0, // decisions
13436        ]))
13437        .deserialize_parser()
13438        .expect("artificial parser ATN should deserialize")
13439    }
13440
13441    fn noop_action_then_token_then_eof_atn() -> Atn {
13442        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13443            4, 1, 2, // version, parser, max token type
13444            4, // states
13445            2, 0, // rule start
13446            1, 0, // basic
13447            1, 0, // basic
13448            7, 0, // rule stop
13449            0, // non-greedy states
13450            0, // precedence states
13451            1, // rules
13452            0, // rule 0 start
13453            0, // modes
13454            0, // sets
13455            3, // transitions
13456            0, 1, 6, 0, -1, 0, // no-op parser action
13457            1, 2, 5, 1, 0, 0, // match token 1
13458            2, 3, 5, -1, 0, 0, // match EOF
13459            0, // decisions
13460        ]))
13461        .deserialize_parser()
13462        .expect("artificial no-op action ATN should deserialize")
13463    }
13464
13465    fn two_alt_decision_atn() -> Atn {
13466        let mut atn = ParserAtnBuilder::new(2);
13467        assert_eq!(
13468            atn.add_state(AtnStateKind::RuleStart, Some(0))
13469                .expect("state")
13470                .index(),
13471            0
13472        );
13473        assert_eq!(
13474            atn.add_state(AtnStateKind::BlockStart, Some(0))
13475                .expect("state")
13476                .index(),
13477            1
13478        );
13479        assert_eq!(
13480            atn.add_state(AtnStateKind::Basic, Some(0))
13481                .expect("state")
13482                .index(),
13483            2
13484        );
13485        assert_eq!(
13486            atn.add_state(AtnStateKind::Basic, Some(0))
13487                .expect("state")
13488                .index(),
13489            3
13490        );
13491        assert_eq!(
13492            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13493                .expect("state")
13494                .index(),
13495            4
13496        );
13497        assert_eq!(
13498            atn.add_state(AtnStateKind::RuleStop, Some(0))
13499                .expect("state")
13500                .index(),
13501            5
13502        );
13503        atn.set_rule_to_start_state(vec![0])
13504            .expect("rule start states");
13505        atn.set_rule_to_stop_state(vec![5])
13506            .expect("rule stop states");
13507        atn.add_decision_state(1).expect("decision state");
13508        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13509            .expect("transition");
13510        atn.add_transition(
13511            1,
13512            ParserTransitionSpec::Atom {
13513                target: 2,
13514                label: 1,
13515            },
13516        )
13517        .expect("transition");
13518        atn.add_transition(
13519            1,
13520            ParserTransitionSpec::Atom {
13521                target: 3,
13522                label: 2,
13523            },
13524        )
13525        .expect("transition");
13526        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
13527            .expect("transition");
13528        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13529            .expect("transition");
13530        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13531            .expect("transition");
13532        finish_atn(atn)
13533    }
13534
13535    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
13536    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
13537    fn optional_then_b_eof_atn() -> Atn {
13538        let mut atn = ParserAtnBuilder::new(3);
13539        assert_eq!(
13540            atn.add_state(AtnStateKind::RuleStart, Some(0))
13541                .expect("state")
13542                .index(),
13543            0
13544        );
13545        assert_eq!(
13546            atn.add_state(AtnStateKind::BlockStart, Some(0))
13547                .expect("state")
13548                .index(),
13549            1
13550        );
13551        assert_eq!(
13552            atn.add_state(AtnStateKind::Basic, Some(0))
13553                .expect("state")
13554                .index(),
13555            2
13556        );
13557        assert_eq!(
13558            atn.add_state(AtnStateKind::Basic, Some(0))
13559                .expect("state")
13560                .index(),
13561            3
13562        );
13563        assert_eq!(
13564            atn.add_state(AtnStateKind::Basic, Some(0))
13565                .expect("state")
13566                .index(),
13567            4
13568        );
13569        assert_eq!(
13570            atn.add_state(AtnStateKind::RuleStop, Some(0))
13571                .expect("state")
13572                .index(),
13573            5
13574        );
13575        atn.set_rule_to_start_state(vec![0])
13576            .expect("rule start states");
13577        atn.set_rule_to_stop_state(vec![5])
13578            .expect("rule stop states");
13579        atn.add_decision_state(1).expect("decision state");
13580        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13581            .expect("transition");
13582        // Optional block: match A then fall through, or skip straight to state 3.
13583        atn.add_transition(
13584            1,
13585            ParserTransitionSpec::Atom {
13586                target: 3,
13587                label: 1,
13588            },
13589        )
13590        .expect("transition");
13591        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
13592            .expect("transition");
13593        // Match B, then EOF.
13594        atn.add_transition(
13595            3,
13596            ParserTransitionSpec::Atom {
13597                target: 4,
13598                label: 2,
13599            },
13600        )
13601        .expect("transition");
13602        atn.add_transition(
13603            4,
13604            ParserTransitionSpec::Atom {
13605                target: 5,
13606                label: TOKEN_EOF,
13607            },
13608        )
13609        .expect("transition");
13610        finish_atn(atn)
13611    }
13612
13613    #[test]
13614    fn sync_decision_deletes_only_a_single_token() {
13615        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
13616        // expected. `(A)? B EOF` at the optional-block decision:
13617        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
13618        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
13619        //               without consuming and records the expected set for the
13620        //               subsequent mismatch (the parser must not over-consume both
13621        //               `C`s and accept the input).
13622        let atn = optional_then_b_eof_atn();
13623
13624        let mut single = mini_parser(vec![
13625            TestToken::new(3).with_text("c"),
13626            TestToken::new(2).with_text("b"),
13627            TestToken::eof("parser-test", 1, 2, 2),
13628        ]);
13629        single.rule_context_stack = vec![RuleContextFrame {
13630            rule_index: 0,
13631            invoking_state: 0,
13632        }];
13633        let children = single
13634            .sync_decision(&atn, 1, true, false)
13635            .expect("single extraneous token recovers");
13636        assert_eq!(children.len(), 1);
13637        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
13638        assert_eq!(single.number_of_syntax_errors(), 1);
13639        // Exactly one token consumed (the cursor now sits on `b`).
13640        assert_eq!(single.la(1), 2);
13641
13642        let mut double = mini_parser(vec![
13643            TestToken::new(3).with_text("c"),
13644            TestToken::new(3).with_text("c"),
13645            TestToken::new(2).with_text("b"),
13646            TestToken::eof("parser-test", 1, 3, 3),
13647        ]);
13648        double.rule_context_stack = vec![RuleContextFrame {
13649            rule_index: 0,
13650            invoking_state: 0,
13651        }];
13652        let result = double.sync_decision(&atn, 1, true, false);
13653        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
13654        // consume either `c`. It reports the mismatch at the first `c` (so the parser
13655        // does not over-consume both and accept the input). Nothing is consumed, so
13656        // the cursor still sits on the first `c` for rule-level recovery.
13657        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
13658        match error {
13659            AntlrError::ParserError { message, .. } => {
13660                assert!(message.starts_with("mismatched input"), "got: {message}");
13661            }
13662            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
13663        }
13664        assert_eq!(double.la(1), 3);
13665    }
13666
13667    /// The real serialized ATN that `antlr4-rust-gen` emits for
13668    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
13669    /// the loop is `EOF`. The loop decision is state 5.
13670    fn star_loop_then_eof_atn() -> Atn {
13671        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13672            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,
13673            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,
13674            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,
13675            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
13676        ]))
13677        .deserialize_parser()
13678        .expect("star-loop-then-EOF ATN should deserialize")
13679    }
13680
13681    /// ATN for `s : a+ Y ; a : X ;`.
13682    ///
13683    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
13684    /// `+` loop must not treat that zero-width child as a successful iteration
13685    /// and then re-enter the loop at the same token index.
13686    fn plus_loop_with_recovering_body_atn() -> Atn {
13687        let mut atn = ParserAtnBuilder::new(2);
13688        assert_eq!(
13689            atn.add_state(AtnStateKind::RuleStart, Some(0))
13690                .expect("state")
13691                .index(),
13692            0
13693        );
13694        assert_eq!(
13695            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
13696                .expect("state")
13697                .index(),
13698            1
13699        );
13700        assert_eq!(
13701            atn.add_state(AtnStateKind::Basic, Some(0))
13702                .expect("state")
13703                .index(),
13704            2
13705        );
13706        assert_eq!(
13707            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13708                .expect("state")
13709                .index(),
13710            3
13711        );
13712        assert_eq!(
13713            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
13714                .expect("state")
13715                .index(),
13716            4
13717        );
13718        assert_eq!(
13719            atn.add_state(AtnStateKind::LoopEnd, Some(0))
13720                .expect("state")
13721                .index(),
13722            5
13723        );
13724        assert_eq!(
13725            atn.add_state(AtnStateKind::RuleStop, Some(0))
13726                .expect("state")
13727                .index(),
13728            6
13729        );
13730        assert_eq!(
13731            atn.add_state(AtnStateKind::RuleStart, Some(1))
13732                .expect("state")
13733                .index(),
13734            7
13735        );
13736        assert_eq!(
13737            atn.add_state(AtnStateKind::Basic, Some(1))
13738                .expect("state")
13739                .index(),
13740            8
13741        );
13742        assert_eq!(
13743            atn.add_state(AtnStateKind::RuleStop, Some(1))
13744                .expect("state")
13745                .index(),
13746            9
13747        );
13748        atn.set_rule_to_start_state(vec![0, 7])
13749            .expect("rule start states");
13750        atn.set_rule_to_stop_state(vec![6, 9])
13751            .expect("rule stop states");
13752        atn.set_end_state(1, 3).expect("block end state");
13753        atn.set_loop_back_state(5, 4).expect("loop back state");
13754        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13755            .expect("transition");
13756        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13757            .expect("transition");
13758        atn.add_transition(
13759            2,
13760            ParserTransitionSpec::Rule {
13761                target: 7,
13762                rule_index: 1,
13763                follow_state: 3,
13764                precedence: 0,
13765            },
13766        )
13767        .expect("transition");
13768        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13769            .expect("transition");
13770        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
13771            .expect("transition");
13772        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13773            .expect("transition");
13774        atn.add_transition(
13775            5,
13776            ParserTransitionSpec::Atom {
13777                target: 6,
13778                label: 2,
13779            },
13780        )
13781        .expect("transition");
13782        atn.add_transition(
13783            7,
13784            ParserTransitionSpec::Atom {
13785                target: 8,
13786                label: 1,
13787            },
13788        )
13789        .expect("transition");
13790        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13791            .expect("transition");
13792        finish_atn(atn)
13793    }
13794
13795    #[test]
13796    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
13797        let atn = plus_loop_with_recovering_body_atn();
13798        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
13799
13800        let error = parser
13801            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
13802            .expect_err("EOF recovery should report a bounded mismatch");
13803
13804        let AntlrError::ParserError { message, .. } = error else {
13805            panic!("expected ParserError, got {error:?}");
13806        };
13807        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
13808        assert_eq!(parser.number_of_syntax_errors(), 1);
13809        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
13810    }
13811
13812    #[test]
13813    fn sync_decision_deletes_token_before_eof_at_loop_back() {
13814        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
13815        // At the loop ENTRY (loop_back = false) a single unexpected token before
13816        // EOF is deleted as an error node (then the generated EOF match consumes
13817        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
13818        // EOF must be a valid scan-stop for this to fire.
13819        let atn = star_loop_then_eof_atn();
13820        let mut parser = mini_parser(vec![
13821            TestToken::new(2).with_text("c"),
13822            TestToken::eof("parser-test", 1, 1, 1),
13823        ]);
13824        parser.rule_context_stack = vec![RuleContextFrame {
13825            rule_index: 0,
13826            invoking_state: 0,
13827        }];
13828        let children = parser
13829            .sync_decision(&atn, 5, true, false)
13830            .expect("single token before EOF recovers");
13831        assert_eq!(children.len(), 1);
13832        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
13833        assert_eq!(parser.number_of_syntax_errors(), 1);
13834        assert_eq!(
13835            parser.la(1),
13836            TOKEN_EOF,
13837            "EOF is left for the rule's EOF match"
13838        );
13839    }
13840
13841    #[test]
13842    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
13843        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
13844        // single-token deletion, which fails because LA(2) = `c` is not expected —
13845        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
13846        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
13847        let atn = star_loop_then_eof_atn();
13848        let mut parser = mini_parser(vec![
13849            TestToken::new(2).with_text("c"),
13850            TestToken::new(2).with_text("c"),
13851            TestToken::eof("parser-test", 1, 2, 2),
13852        ]);
13853        parser.rule_context_stack = vec![RuleContextFrame {
13854            rule_index: 0,
13855            invoking_state: 0,
13856        }];
13857        let error = parser
13858            .sync_decision(&atn, 5, true, false)
13859            .expect_err("two tokens at the loop entry must not be deleted");
13860        match error {
13861            AntlrError::ParserError { message, .. } => {
13862                assert!(message.starts_with("mismatched input"), "got: {message}");
13863            }
13864            other => panic!("expected mismatched-input ParserError, got {other:?}"),
13865        }
13866        assert_eq!(
13867            parser.la(1),
13868            2,
13869            "nothing consumed; cursor still on first `c`"
13870        );
13871    }
13872
13873    #[test]
13874    fn sync_decision_consumes_until_eof_at_loop_back() {
13875        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
13876        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
13877        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
13878        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
13879        // post-`a` state directly: `c c <EOF>` with loop_back = true.
13880        let atn = star_loop_then_eof_atn();
13881        let mut parser = mini_parser(vec![
13882            TestToken::new(2).with_text("c"),
13883            TestToken::new(2).with_text("c"),
13884            TestToken::eof("parser-test", 1, 2, 2),
13885        ]);
13886        parser.rule_context_stack = vec![RuleContextFrame {
13887            rule_index: 0,
13888            invoking_state: 0,
13889        }];
13890        let children = parser
13891            .sync_decision(&atn, 5, false, true)
13892            .expect("loop-back multi-token deletion recovers onto EOF");
13893        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
13894        assert!(
13895            children
13896                .iter()
13897                .all(|child| parser.node(*child).kind() == NodeKind::Error)
13898        );
13899        assert_eq!(parser.number_of_syntax_errors(), 1);
13900        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
13901    }
13902
13903    fn predicate_after_token_atn() -> Atn {
13904        let mut atn = ParserAtnBuilder::new(2);
13905        assert_eq!(
13906            atn.add_state(AtnStateKind::RuleStart, Some(0))
13907                .expect("state")
13908                .index(),
13909            0
13910        );
13911        assert_eq!(
13912            atn.add_state(AtnStateKind::Basic, Some(0))
13913                .expect("state")
13914                .index(),
13915            1
13916        );
13917        assert_eq!(
13918            atn.add_state(AtnStateKind::Basic, Some(0))
13919                .expect("state")
13920                .index(),
13921            2
13922        );
13923        assert_eq!(
13924            atn.add_state(AtnStateKind::Basic, Some(0))
13925                .expect("state")
13926                .index(),
13927            3
13928        );
13929        assert_eq!(
13930            atn.add_state(AtnStateKind::RuleStop, Some(0))
13931                .expect("state")
13932                .index(),
13933            4
13934        );
13935        atn.set_rule_to_start_state(vec![0])
13936            .expect("rule start states");
13937        atn.set_rule_to_stop_state(vec![4])
13938            .expect("rule stop states");
13939        atn.add_transition(
13940            0,
13941            ParserTransitionSpec::Atom {
13942                target: 1,
13943                label: 1,
13944            },
13945        )
13946        .expect("transition");
13947        atn.add_transition(
13948            1,
13949            ParserTransitionSpec::Predicate {
13950                target: 2,
13951                rule_index: 0,
13952                pred_index: 0,
13953                context_dependent: false,
13954            },
13955        )
13956        .expect("transition");
13957        atn.add_transition(
13958            2,
13959            ParserTransitionSpec::Atom {
13960                target: 3,
13961                label: 2,
13962            },
13963        )
13964        .expect("transition");
13965        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13966            .expect("transition");
13967        finish_atn(atn)
13968    }
13969
13970    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
13971        let mut atn = ParserAtnBuilder::new(1);
13972        for (state_number, kind) in [
13973            (0, AtnStateKind::RuleStart),
13974            (1, AtnStateKind::BlockStart),
13975            (2, AtnStateKind::Basic),
13976            (3, AtnStateKind::Basic),
13977            (4, AtnStateKind::Basic),
13978            (5, AtnStateKind::Basic),
13979            (6, AtnStateKind::BlockEnd),
13980            (7, AtnStateKind::RuleStop),
13981        ] {
13982            assert_eq!(
13983                atn.add_state(kind, Some(0)).expect("state").index(),
13984                state_number
13985            );
13986        }
13987        atn.set_rule_to_start_state(vec![0])
13988            .expect("rule start states");
13989        atn.set_rule_to_stop_state(vec![7])
13990            .expect("rule stop states");
13991        atn.add_decision_state(1).expect("decision state");
13992        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13993            .expect("transition");
13994        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13995            .expect("transition");
13996        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
13997            .expect("transition");
13998        atn.add_transition(
13999            2,
14000            ParserTransitionSpec::Predicate {
14001                target: 4,
14002                rule_index: 0,
14003                pred_index: pred_indexes[0],
14004                context_dependent: false,
14005            },
14006        )
14007        .expect("transition");
14008        atn.add_transition(
14009            3,
14010            ParserTransitionSpec::Predicate {
14011                target: 5,
14012                rule_index: 0,
14013                pred_index: pred_indexes[1],
14014                context_dependent: false,
14015            },
14016        )
14017        .expect("transition");
14018        atn.add_transition(
14019            4,
14020            ParserTransitionSpec::Atom {
14021                target: 6,
14022                label: 1,
14023            },
14024        )
14025        .expect("transition");
14026        atn.add_transition(
14027            5,
14028            ParserTransitionSpec::Atom {
14029                target: 6,
14030                label: 1,
14031            },
14032        )
14033        .expect("transition");
14034        atn.add_transition(
14035            6,
14036            ParserTransitionSpec::Atom {
14037                target: 7,
14038                label: TOKEN_EOF,
14039            },
14040        )
14041        .expect("transition");
14042        finish_atn(atn)
14043    }
14044
14045    fn nested_nullable_context_atn() -> Atn {
14046        let mut atn = ParserAtnBuilder::new(1);
14047        for state_number in 0..=20 {
14048            let kind = match state_number {
14049                0 | 10 | 16 => AtnStateKind::RuleStart,
14050                9 | 15 | 20 => AtnStateKind::RuleStop,
14051                _ => AtnStateKind::Basic,
14052            };
14053            let rule_index = match state_number {
14054                0..=9 => 0,
14055                10..=15 => 1,
14056                _ => 2,
14057            };
14058            assert_eq!(
14059                atn.add_state(kind, Some(rule_index))
14060                    .expect("state")
14061                    .index(),
14062                state_number
14063            );
14064        }
14065        atn.set_rule_to_start_state(vec![0, 10, 16])
14066            .expect("rule start states");
14067        atn.set_rule_to_stop_state(vec![9, 15, 20])
14068            .expect("rule stop states");
14069        atn.add_transition(
14070            1,
14071            ParserTransitionSpec::Rule {
14072                target: 10,
14073                rule_index: 1,
14074                follow_state: 8,
14075                precedence: 0,
14076            },
14077        )
14078        .expect("transition");
14079        atn.add_transition(
14080            8,
14081            ParserTransitionSpec::Atom {
14082                target: 9,
14083                label: 1,
14084            },
14085        )
14086        .expect("transition");
14087        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14088            .expect("transition");
14089        atn.add_transition(
14090            2,
14091            ParserTransitionSpec::Rule {
14092                target: 16,
14093                rule_index: 2,
14094                follow_state: 14,
14095                precedence: 0,
14096            },
14097        )
14098        .expect("transition");
14099        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
14100            .expect("transition");
14101        finish_atn(atn)
14102    }
14103
14104    fn generated_match_recovery_atn() -> Atn {
14105        let mut atn = ParserAtnBuilder::new(2);
14106        assert_eq!(
14107            atn.add_state(AtnStateKind::RuleStart, Some(0))
14108                .expect("state")
14109                .index(),
14110            0
14111        );
14112        assert_eq!(
14113            atn.add_state(AtnStateKind::Basic, Some(0))
14114                .expect("state")
14115                .index(),
14116            1
14117        );
14118        assert_eq!(
14119            atn.add_state(AtnStateKind::Basic, Some(0))
14120                .expect("state")
14121                .index(),
14122            2
14123        );
14124        assert_eq!(
14125            atn.add_state(AtnStateKind::RuleStop, Some(0))
14126                .expect("state")
14127                .index(),
14128            3
14129        );
14130        assert_eq!(
14131            atn.add_state(AtnStateKind::RuleStart, Some(1))
14132                .expect("state")
14133                .index(),
14134            4
14135        );
14136        assert_eq!(
14137            atn.add_state(AtnStateKind::RuleStop, Some(1))
14138                .expect("state")
14139                .index(),
14140            5
14141        );
14142        atn.set_rule_to_start_state(vec![0, 4])
14143            .expect("rule start states");
14144        atn.set_rule_to_stop_state(vec![3, 5])
14145            .expect("rule stop states");
14146        atn.add_transition(
14147            1,
14148            ParserTransitionSpec::Rule {
14149                target: 4,
14150                rule_index: 1,
14151                follow_state: 2,
14152                precedence: 0,
14153            },
14154        )
14155        .expect("transition");
14156        atn.add_transition(
14157            2,
14158            ParserTransitionSpec::Atom {
14159                target: 3,
14160                label: TOKEN_EOF,
14161            },
14162        )
14163        .expect("transition");
14164        finish_atn(atn)
14165    }
14166
14167    fn complement_set_atn() -> Atn {
14168        let mut atn = ParserAtnBuilder::new(1);
14169        assert_eq!(
14170            atn.add_state(AtnStateKind::RuleStart, Some(0))
14171                .expect("state")
14172                .index(),
14173            0
14174        );
14175        assert_eq!(
14176            atn.add_state(AtnStateKind::RuleStop, Some(0))
14177                .expect("state")
14178                .index(),
14179            1
14180        );
14181        atn.set_rule_to_start_state(vec![0])
14182            .expect("rule start states");
14183        atn.set_rule_to_stop_state(vec![1])
14184            .expect("rule stop states");
14185        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
14186        atn.add_transition(
14187            0,
14188            ParserTransitionSpec::NotSet {
14189                target: 1,
14190                set: excluded,
14191            },
14192        )
14193        .expect("transition");
14194        finish_atn(atn)
14195    }
14196
14197    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
14198    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
14199    fn wildcard_then_eof_atn() -> Atn {
14200        let mut atn = ParserAtnBuilder::new(1);
14201        assert_eq!(
14202            atn.add_state(AtnStateKind::RuleStart, Some(0))
14203                .expect("state")
14204                .index(),
14205            0
14206        );
14207        assert_eq!(
14208            atn.add_state(AtnStateKind::RuleStop, Some(0))
14209                .expect("state")
14210                .index(),
14211            1
14212        );
14213        assert_eq!(
14214            atn.add_state(AtnStateKind::Basic, Some(0))
14215                .expect("state")
14216                .index(),
14217            2
14218        );
14219        atn.set_rule_to_start_state(vec![0])
14220            .expect("rule start states");
14221        atn.set_rule_to_stop_state(vec![1])
14222            .expect("rule stop states");
14223        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
14224            .expect("transition");
14225        atn.add_transition(
14226            2,
14227            ParserTransitionSpec::Atom {
14228                target: 1,
14229                label: TOKEN_EOF,
14230            },
14231        )
14232        .expect("transition");
14233        finish_atn(atn)
14234    }
14235
14236    #[test]
14237    fn parser_matches_token_and_reports_mismatch() {
14238        let source = Source {
14239            tokens: vec![
14240                TestToken::new(1).with_text("x"),
14241                TestToken::eof("parser-test", 1, 1, 1),
14242            ],
14243            index: 0,
14244        };
14245        let data = RecognizerData::new(
14246            "Mini.g4",
14247            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14248        );
14249        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
14250        let matched = parser.match_token(1).expect("token 1 should match");
14251        assert_eq!(parser.node(matched).text(), "x");
14252        assert!(parser.match_token(1).is_err());
14253    }
14254
14255    #[test]
14256    fn parser_matches_token_sets() {
14257        let mut parser = mini_parser(vec![
14258            TestToken::new(1).with_text("x"),
14259            TestToken::eof("parser-test", 1, 1, 1),
14260        ]);
14261
14262        let matched = parser
14263            .match_set(&[(1, 1), (3, 4)])
14264            .expect("token set should match");
14265        assert_eq!(parser.node(matched).text(), "x");
14266        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
14267    }
14268
14269    #[test]
14270    fn generated_rule_api_tracks_state_and_precedence() {
14271        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14272
14273        let context = parser.enter_rule(7, 2);
14274        assert_eq!(context.rule_index(), 2);
14275        assert_eq!(parser.state(), 7);
14276        assert_eq!(
14277            parser.rule_context_stack,
14278            vec![RuleContextFrame {
14279                rule_index: 2,
14280                invoking_state: 7
14281            }]
14282        );
14283
14284        let recursive = parser.enter_recursion_rule(11, 3, 4);
14285        assert_eq!(recursive.rule_index(), 3);
14286        assert!(parser.precpred(4));
14287        assert!(parser.precpred(5));
14288        assert!(!parser.precpred(3));
14289
14290        let next = parser.push_new_recursion_context(13, 3);
14291        assert_eq!(next.invoking_state(), 13);
14292        parser.unroll_recursion_context();
14293        assert_eq!(parser.precedence_stack, vec![0]);
14294        assert_eq!(
14295            parser.rule_context_stack,
14296            vec![RuleContextFrame {
14297                rule_index: 2,
14298                invoking_state: 7
14299            }]
14300        );
14301
14302        parser.exit_rule();
14303        assert!(parser.rule_context_stack.is_empty());
14304    }
14305
14306    #[test]
14307    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
14308        let mut parser = mini_parser(vec![
14309            TestToken::new(1).with_text("x"),
14310            TestToken::eof("parser-test", 1, 1, 1),
14311        ]);
14312        let matched = parser.match_token(1).expect("token should match");
14313        assert_eq!(parser.node(matched).text(), "x");
14314        parser.record_generated_syntax_error();
14315        parser.set_int_member(7, 11);
14316        parser.set_build_parse_trees(false);
14317        parser.set_report_diagnostic_errors(true);
14318        parser.set_prediction_mode(PredictionMode::Sll);
14319        parser.set_bail_on_error(true);
14320        let _context = parser.enter_recursion_rule(9, 0, 4);
14321        parser.pending_invoking_states.push(5);
14322        parser.unknown_predicate_hits.push((0, 1));
14323        parser.unhandled_action_hits.push((0, 2));
14324
14325        parser.reset();
14326
14327        assert_eq!(parser.input.index(), 0);
14328        assert_eq!(parser.la(1), 1);
14329        assert_eq!(parser.state(), -1);
14330        assert_eq!(parser.number_of_syntax_errors(), 0);
14331        assert_eq!(parser.parse_tree_storage().node_count(), 0);
14332        assert!(parser.rule_context_stack.is_empty());
14333        assert!(parser.pending_invoking_states.is_empty());
14334        assert_eq!(parser.precedence_stack, [0]);
14335        assert!(parser.unknown_predicate_hits.is_empty());
14336        assert!(parser.unhandled_action_hits.is_empty());
14337        assert_eq!(parser.int_member(7), Some(11));
14338        assert!(!parser.build_parse_trees());
14339        assert!(parser.report_diagnostic_errors());
14340        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
14341        assert!(parser.bail_on_error());
14342    }
14343
14344    #[test]
14345    fn set_token_stream_replaces_input_and_resets_parser() {
14346        let mut parser = mini_parser(vec![
14347            TestToken::new(1).with_text("old"),
14348            TestToken::eof("parser-test", 1, 1, 1),
14349        ]);
14350        parser.consume();
14351        parser.record_generated_syntax_error();
14352        let replacement = CommonTokenStream::new(Source {
14353            tokens: vec![
14354                TestToken::new(2).with_text("new"),
14355                TestToken::eof("parser-test", 1, 1, 1),
14356            ],
14357            index: 0,
14358        });
14359
14360        parser.set_token_stream(replacement);
14361
14362        assert_eq!(parser.input.index(), 0);
14363        assert_eq!(parser.la(1), 2);
14364        assert_eq!(parser.input.text_all(), "new");
14365        assert_eq!(parser.number_of_syntax_errors(), 0);
14366    }
14367
14368    #[test]
14369    fn active_invocation_states_exclude_the_root_frame() {
14370        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14371
14372        let _root = parser.enter_rule(0, 0);
14373        assert!(parser.active_invocation_states().is_empty());
14374
14375        let marker = parser.push_invoking_state(6);
14376        let _child = parser.enter_rule(2, 1);
14377        parser.discard_invoking_state(marker);
14378        assert_eq!(parser.active_invocation_states(), [6]);
14379
14380        let marker = parser.push_invoking_state(13);
14381        let _grandchild = parser.enter_rule(4, 2);
14382        parser.discard_invoking_state(marker);
14383        assert_eq!(parser.active_invocation_states(), [13, 6]);
14384
14385        parser.exit_rule();
14386        parser.exit_rule();
14387        parser.exit_rule();
14388    }
14389
14390    #[test]
14391    fn parser_predicates_support_token_adjacency() {
14392        let mut parser = mini_parser(vec![
14393            TestToken::new(1).with_text("=").with_span(0, 0),
14394            TestToken::new(1).with_text(">").with_span(1, 1),
14395            TestToken::eof("parser-test", 2, 1, 2),
14396        ]);
14397        parser.consume();
14398        parser.consume();
14399
14400        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
14401
14402        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14403
14404        let mut parser = mini_parser(vec![
14405            TestToken::new(1).with_text("=").with_span(0, 0),
14406            TestToken::new(1)
14407                .with_text(" ")
14408                .with_channel(HIDDEN_CHANNEL)
14409                .with_span(1, 1),
14410            TestToken::new(1).with_text(">").with_span(2, 2),
14411            TestToken::eof("parser-test", 3, 1, 3),
14412        ]);
14413        parser.consume();
14414        parser.consume();
14415
14416        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14417    }
14418
14419    #[test]
14420    fn parser_predicates_support_context_child_text_checks() {
14421        let mut parser = mini_parser(vec![
14422            TestToken::new(1).with_text("var"),
14423            TestToken::eof("parser-test", 1, 1, 1),
14424        ]);
14425        let mut context = ParserRuleContext::new(1, 0);
14426        let mut child_context = ParserRuleContext::new(2, 0);
14427        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
14428        parser.tree.add_child(&mut child_context, terminal);
14429        let child = parser.rule_node(child_context);
14430        parser.tree.add_child(&mut context, child);
14431        let predicates = [(
14432            1,
14433            0,
14434            ParserPredicate::ContextChildRuleTextNotEquals {
14435                rule_index: 2,
14436                text: "var",
14437            },
14438        )];
14439
14440        assert!(
14441            !parser.parser_semantic_predicate_matches_with_context_and_local(
14442                &predicates,
14443                1,
14444                0,
14445                &context,
14446                0,
14447            )
14448        );
14449    }
14450
14451    #[test]
14452    fn context_expected_symbols_walks_nullable_parent_contexts() {
14453        let atn = nested_nullable_context_atn();
14454        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
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            RuleContextFrame {
14465                rule_index: 2,
14466                invoking_state: 2,
14467            },
14468        ];
14469
14470        let expected = parser.context_expected_symbols(&atn);
14471
14472        assert!(expected.contains(&1));
14473        assert!(expected.contains(&TOKEN_EOF));
14474    }
14475
14476    #[test]
14477    fn prediction_context_return_states_track_rule_stack_changes() {
14478        let atn = nested_nullable_context_atn();
14479        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14480        parser.rule_context_stack = vec![
14481            RuleContextFrame {
14482                rule_index: 0,
14483                invoking_state: 0,
14484            },
14485            RuleContextFrame {
14486                rule_index: 1,
14487                invoking_state: 1,
14488            },
14489            RuleContextFrame {
14490                rule_index: 2,
14491                invoking_state: 2,
14492            },
14493        ];
14494
14495        let initial_version = parser.rule_context_version();
14496        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14497        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14498        assert_eq!(first, second);
14499        assert_eq!(parser.rule_context_version(), initial_version);
14500
14501        parser.exit_rule();
14502        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14503        assert_ne!(first, after_pop);
14504        assert_ne!(parser.rule_context_version(), initial_version);
14505    }
14506
14507    #[test]
14508    fn generated_match_token_recovers_missing_token_from_context_follow() {
14509        let atn = generated_match_recovery_atn();
14510        let data = RecognizerData::new(
14511            "Mini.g4",
14512            Vocabulary::new(
14513                [None, Some("'X'"), Some("'Y'")],
14514                [None, Some("X"), Some("Y")],
14515                [None::<&str>, None, None],
14516            ),
14517        );
14518        let mut parser = BaseParser::new(
14519            CommonTokenStream::new(Source {
14520                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14521                index: 0,
14522            }),
14523            data,
14524        );
14525        parser.rule_context_stack = vec![
14526            RuleContextFrame {
14527                rule_index: 0,
14528                invoking_state: 0,
14529            },
14530            RuleContextFrame {
14531                rule_index: 1,
14532                invoking_state: 1,
14533            },
14534        ];
14535        assert_eq!(parser.number_of_syntax_errors(), 0);
14536
14537        let node = parser
14538            .match_token_recovering(2, 5, &atn)
14539            .expect("generated match should insert missing token");
14540
14541        assert_eq!(node.children().len(), 1);
14542        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
14543        assert_eq!(
14544            node.clone()
14545                .into_child_iter()
14546                .map(|child| parser.node(child).text())
14547                .collect::<Vec<_>>(),
14548            ["<missing 'Y'>"]
14549        );
14550        // Single-token insertion synthesizes a missing token and consumes nothing,
14551        // so no EOF terminal is consumed even though lookahead is EOF.
14552        assert!(!node.consumed_eof());
14553        assert_eq!(parser.la(1), TOKEN_EOF);
14554        assert_eq!(parser.number_of_syntax_errors(), 1);
14555        assert_eq!(
14556            parser.generated_parser_diagnostics,
14557            [ParserDiagnostic {
14558                line: 1,
14559                column: 3,
14560                message: "missing 'Y' at '<EOF>'".to_owned(),
14561            }]
14562        );
14563    }
14564
14565    #[test]
14566    fn generated_match_token_counts_single_token_deletion_recovery() {
14567        let atn = generated_match_recovery_atn();
14568        let data = RecognizerData::new(
14569            "Mini.g4",
14570            Vocabulary::new(
14571                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
14572                [None, Some("X"), Some("Y"), Some("Z")],
14573                [None::<&str>, None, None, None],
14574            ),
14575        );
14576        let mut parser = BaseParser::new(
14577            CommonTokenStream::new(Source {
14578                tokens: vec![
14579                    TestToken::new(3).with_text("z"),
14580                    TestToken::new(2).with_text("y"),
14581                    TestToken::eof("parser-test", 3, 1, 3),
14582                ],
14583                index: 0,
14584            }),
14585            data,
14586        );
14587
14588        let node = parser
14589            .match_token_recovering(2, 5, &atn)
14590            .expect("generated match should delete the extraneous token");
14591
14592        assert_eq!(node.children().len(), 2);
14593        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
14594        assert_eq!(parser.node(node.children()[0]).text(), "z");
14595        assert_eq!(parser.node(node.children()[1]).text(), "y");
14596        assert_eq!(
14597            node.into_child_iter()
14598                .map(|child| parser.node(child).text())
14599                .collect::<Vec<_>>(),
14600            ["z", "y"]
14601        );
14602        assert_eq!(parser.number_of_syntax_errors(), 1);
14603    }
14604
14605    #[test]
14606    fn generated_match_token_iterates_single_success_without_a_children_vec() {
14607        let atn = generated_match_recovery_atn();
14608        let data = RecognizerData::new(
14609            "Mini.g4",
14610            Vocabulary::new(
14611                [None, Some("'X'"), Some("'Y'")],
14612                [None, Some("X"), Some("Y")],
14613                [None::<&str>, None, None],
14614            ),
14615        );
14616        let mut parser = BaseParser::new(
14617            CommonTokenStream::new(Source {
14618                tokens: vec![
14619                    TestToken::new(2).with_text("y"),
14620                    TestToken::eof("parser-test", 1, 1, 1),
14621                ],
14622                index: 0,
14623            }),
14624            data,
14625        );
14626
14627        let node = parser
14628            .match_token_recovering(2, 5, &atn)
14629            .expect("generated match should consume the expected token");
14630
14631        assert_eq!(
14632            node.into_child_iter()
14633                .map(|child| parser.node(child).text())
14634                .collect::<Vec<_>>(),
14635            ["y"]
14636        );
14637        assert_eq!(parser.number_of_syntax_errors(), 0);
14638    }
14639
14640    #[test]
14641    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
14642        let atn = generated_match_recovery_atn();
14643        let data = RecognizerData::new(
14644            "Mini.g4",
14645            Vocabulary::new(
14646                [None, Some("'X'"), Some("'Y'")],
14647                [None, Some("X"), Some("Y")],
14648                [None::<&str>, None, None],
14649            ),
14650        );
14651        let mut parser = BaseParser::new(
14652            CommonTokenStream::new(Source {
14653                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14654                index: 0,
14655            }),
14656            data,
14657        );
14658        parser.rule_context_stack = vec![
14659            RuleContextFrame {
14660                rule_index: 0,
14661                invoking_state: 0,
14662            },
14663            RuleContextFrame {
14664                rule_index: 1,
14665                invoking_state: 1,
14666            },
14667        ];
14668        let marker = parser.generated_diagnostics_checkpoint();
14669
14670        let _ = parser
14671            .match_token_recovering(2, 5, &atn)
14672            .expect("generated match should insert missing token");
14673        assert_eq!(parser.number_of_syntax_errors(), 1);
14674
14675        parser.restore_generated_diagnostics(marker);
14676
14677        assert_eq!(parser.number_of_syntax_errors(), 0);
14678        assert!(parser.generated_parser_diagnostics.is_empty());
14679    }
14680
14681    #[test]
14682    fn generated_prediction_diagnostics_use_adaptive_context() {
14683        let atn = two_alt_decision_atn();
14684        let data = RecognizerData::new(
14685            "Mini.g4",
14686            Vocabulary::new(
14687                [None, Some("'x'"), Some("'y'")],
14688                [None, Some("X"), Some("Y")],
14689                [None::<&str>, None, None],
14690            ),
14691        )
14692        .with_rule_names(["s"]);
14693        let mut parser = BaseParser::new(
14694            CommonTokenStream::new(Source {
14695                tokens: vec![
14696                    TestToken::new(1)
14697                        .with_text("x")
14698                        .with_position(1, 0)
14699                        .with_span(0, 0),
14700                    TestToken::new(2)
14701                        .with_text("y")
14702                        .with_position(1, 2)
14703                        .with_span(1, 1),
14704                    TestToken::eof("parser-test", 2, 1, 3),
14705                ],
14706                index: 0,
14707            }),
14708            data,
14709        );
14710        parser.set_report_diagnostic_errors(true);
14711
14712        parser.record_generated_prediction_diagnostic(
14713            &atn,
14714            1,
14715            &ParserAtnPrediction {
14716                alt: 1,
14717                requires_full_context: true,
14718                has_semantic_context: false,
14719                diagnostic: Some(ParserAtnPredictionDiagnostic {
14720                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
14721                    start_index: 0,
14722                    sll_stop_index: 1,
14723                    ll_stop_index: 0,
14724                    conflicting_alts: vec![1, 2],
14725                    exact: false,
14726                }),
14727            },
14728        );
14729        // Ambiguities from the default LL prediction mode are non-exact, so —
14730        // matching Java's exactOnly DiagnosticErrorListener — only the
14731        // attempting-full-context line is reported. Exact-ambiguity mode
14732        // reports the ambiguity itself.
14733        parser.record_generated_prediction_diagnostic(
14734            &atn,
14735            1,
14736            &ParserAtnPrediction {
14737                alt: 1,
14738                requires_full_context: true,
14739                has_semantic_context: false,
14740                diagnostic: Some(ParserAtnPredictionDiagnostic {
14741                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
14742                    start_index: 0,
14743                    sll_stop_index: 1,
14744                    ll_stop_index: 1,
14745                    conflicting_alts: vec![1, 2],
14746                    exact: false,
14747                }),
14748            },
14749        );
14750
14751        assert_eq!(
14752            parser.generated_parser_diagnostics,
14753            [
14754                ParserDiagnostic {
14755                    line: 1,
14756                    column: 2,
14757                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
14758                },
14759                ParserDiagnostic {
14760                    line: 1,
14761                    column: 0,
14762                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
14763                },
14764                ParserDiagnostic {
14765                    line: 1,
14766                    column: 2,
14767                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
14768                },
14769            ]
14770        );
14771    }
14772
14773    #[test]
14774    fn generated_match_not_set_recovers_empty_complement_at_eof() {
14775        let atn = complement_set_atn();
14776        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14777        parser.rule_context_stack = vec![RuleContextFrame {
14778            rule_index: 0,
14779            invoking_state: 0,
14780        }];
14781
14782        let node = parser
14783            .match_not_set_recovering(&[(1, 1)], 1, 1, 1, &atn)
14784            .expect("empty complement should recover at EOF");
14785
14786        assert_eq!(node.children().len(), 1);
14787        // Recovery synthesizes a missing token without consuming EOF, so the
14788        // enclosing rule must not record EOF as its stop token.
14789        assert!(!node.consumed_eof());
14790        assert_eq!(parser.la(1), TOKEN_EOF);
14791        assert_eq!(
14792            parser.generated_parser_diagnostics,
14793            [ParserDiagnostic {
14794                line: 1,
14795                column: 1,
14796                message: "missing {} at '<EOF>'".to_owned(),
14797            }]
14798        );
14799    }
14800
14801    #[test]
14802    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
14803        // `start : . EOF ;` on empty input. The wildcard is modeled as an
14804        // empty-complement not-set; at EOF the follow state (the explicit EOF
14805        // match) expects EOF, so even in the start rule recovery must perform
14806        // single-token insertion (`<missing ...>`) rather than aborting — matching
14807        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
14808        let atn = wildcard_then_eof_atn();
14809        let data = RecognizerData::new(
14810            "Mini.g4",
14811            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14812        );
14813        let mut parser = BaseParser::new(
14814            CommonTokenStream::new(Source {
14815                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
14816                index: 0,
14817            }),
14818            data,
14819        );
14820        parser.rule_context_stack = vec![RuleContextFrame {
14821            rule_index: 0,
14822            invoking_state: 0,
14823        }];
14824
14825        let node = parser
14826            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
14827            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
14828
14829        // A single `<missing ...>` error node is inserted; EOF is not consumed.
14830        assert_eq!(node.children().len(), 1);
14831        assert!(!node.consumed_eof());
14832        assert!(
14833            parser
14834                .node(node.children()[0])
14835                .text()
14836                .starts_with("<missing")
14837        );
14838        assert_eq!(parser.la(1), TOKEN_EOF);
14839        assert_eq!(
14840            parser.generated_parser_diagnostics,
14841            [ParserDiagnostic {
14842                line: 1,
14843                column: 1,
14844                message: "missing 'x' at '<EOF>'".to_owned(),
14845            }]
14846        );
14847    }
14848
14849    #[test]
14850    fn generated_rule_recovery_consumes_to_parent_follow() {
14851        let atn = generated_match_recovery_atn();
14852        let data = RecognizerData::new(
14853            "Mini.g4",
14854            Vocabulary::new(
14855                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
14856                [None, Some("X"), Some("Y"), Some("Z")],
14857                [None::<&str>, None, None, None],
14858            ),
14859        );
14860        let mut parser = BaseParser::new(
14861            CommonTokenStream::new(Source {
14862                tokens: vec![
14863                    TestToken::new(3).with_text("z"),
14864                    TestToken::eof("parser-test", 1, 1, 1),
14865                ],
14866                index: 0,
14867            }),
14868            data,
14869        );
14870        let _parent = parser.enter_rule(0, 0);
14871        let marker = parser.push_invoking_state(1);
14872        let mut child = parser.enter_rule(4, 1);
14873        parser.discard_invoking_state(marker);
14874
14875        parser.recover_generated_rule(
14876            &mut child,
14877            &atn,
14878            AntlrError::ParserError {
14879                line: 1,
14880                column: 0,
14881                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
14882            },
14883        );
14884        let tree = parser.finish_rule(child, false);
14885
14886        assert_eq!(parser.la(1), TOKEN_EOF);
14887        assert_eq!(
14888            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
14889            "(a z)"
14890        );
14891        assert_eq!(parser.number_of_syntax_errors(), 1);
14892        assert_eq!(
14893            parser.generated_parser_diagnostics,
14894            [ParserDiagnostic {
14895                line: 1,
14896                column: 0,
14897                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
14898            }]
14899        );
14900        parser.exit_rule();
14901    }
14902
14903    #[test]
14904    fn greedy_ll1_alt_handles_nullable_loop_exit() {
14905        let mut body_symbols = TokenBitSet::default();
14906        body_symbols.insert(1);
14907        let entry = DecisionLookahead {
14908            transitions: vec![
14909                TransitionLookSet {
14910                    symbols: body_symbols,
14911                    nullable: false,
14912                },
14913                TransitionLookSet {
14914                    symbols: TokenBitSet::default(),
14915                    nullable: true,
14916                },
14917            ],
14918        };
14919
14920        assert_eq!(ll1_unique_alt(&entry, 2), None);
14921        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
14922        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
14923        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
14924    }
14925
14926    #[test]
14927    fn ordinary_repetition_builds_tree_in_input_order() {
14928        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
14929            let mut parser = mini_parser(repeated_x_tokens(3));
14930            let tree = parser
14931                .parse_atn_rule(&atn, 0)
14932                .expect("ordinary repetition should parse");
14933
14934            let root = parser
14935                .node(tree)
14936                .as_rule()
14937                .expect("entry result should be a rule");
14938            let body_rules = root.child_rules(1).collect::<Vec<_>>();
14939            assert_eq!(root.text(), "xxx<EOF>");
14940            assert_eq!(body_rules.len(), 3);
14941            assert_eq!(
14942                body_rules
14943                    .iter()
14944                    .map(|rule| rule.start_id().expect("body start").index())
14945                    .collect::<Vec<_>>(),
14946                [0, 1, 2]
14947            );
14948            assert_eq!(
14949                body_rules
14950                    .iter()
14951                    .map(|rule| rule.stop_id().expect("body stop").index())
14952                    .collect::<Vec<_>>(),
14953                [0, 1, 2]
14954            );
14955            assert_eq!(parser.number_of_syntax_errors(), 0);
14956        }
14957    }
14958
14959    #[test]
14960    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
14961        const DEPTH: usize = 20_000;
14962
14963        std::thread::Builder::new()
14964            .name("deferred-rule-materialization".to_owned())
14965            .stack_size(256 * 1024)
14966            .spawn(|| {
14967                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
14968                let mut root = FastDeferredNodeId::EMPTY;
14969                for depth in 0..DEPTH {
14970                    root = parser
14971                        .recognition_arena
14972                        .deferred_rule_node(FastDeferredRule {
14973                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
14974                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
14975                            start_index: 0,
14976                            stop_index: None,
14977                            deferred_children: root,
14978                            children: NodeSeqId::EMPTY,
14979                        });
14980                }
14981
14982                let mut children = parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
14983                for expected_rule in (0..DEPTH).rev() {
14984                    let mut nodes = parser.recognition_arena.iter(children);
14985                    let node = nodes.next().expect("nested rule node");
14986                    assert!(nodes.next().is_none(), "each rule has one child");
14987                    let ArenaRecognizedNode::Rule {
14988                        rule_index,
14989                        children: nested,
14990                        ..
14991                    } = parser.recognition_arena.node(node)
14992                    else {
14993                        panic!("expected nested rule");
14994                    };
14995                    assert_eq!(rule_index as usize, expected_rule);
14996                    children = nested;
14997                }
14998                assert!(children.is_empty());
14999            })
15000            .expect("small-stack thread should start")
15001            .join()
15002            .expect("deferred rules should materialize without recursion");
15003    }
15004
15005    #[test]
15006    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
15007        const REPETITIONS: usize = 64;
15008
15009        let atn = ambiguous_ordinary_star_loop_atn();
15010        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15011        let tree = parser
15012            .parse_atn_rule(&atn, 0)
15013            .expect("ambiguous ordinary repetition should parse");
15014
15015        let root = parser
15016            .node(tree)
15017            .as_rule()
15018            .expect("entry result should be a rule");
15019        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
15020        assert_eq!(parser.input.index(), REPETITIONS);
15021        assert!(
15022            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
15023            "equivalent segmentations should keep deferred storage linear"
15024        );
15025        assert_eq!(parser.number_of_syntax_errors(), 0);
15026    }
15027
15028    #[test]
15029    fn long_ordinary_repetition_does_not_consume_native_stack() {
15030        const REPETITIONS: usize = 20_000;
15031
15032        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15033            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15034            parser.set_build_parse_trees(false);
15035            parser
15036                .parse_atn_rule(&atn, 0)
15037                .expect("long ordinary repetition should parse");
15038
15039            assert_eq!(parser.input.index(), REPETITIONS);
15040            assert_eq!(parser.number_of_syntax_errors(), 0);
15041        }
15042    }
15043
15044    #[test]
15045    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
15046        const REPETITIONS: usize = 2_000;
15047        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
15048
15049        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15050            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15051            let tree = parser
15052                .parse_atn_rule(&atn, 0)
15053                .expect("long rule repetition should parse");
15054
15055            let root = parser
15056                .node(tree)
15057                .as_rule()
15058                .expect("entry result should be a rule");
15059            assert_eq!(root.text(), expected_text);
15060            assert_eq!(root.child_rules(1).count(), REPETITIONS);
15061            let first_body = root.child_rules(1).next().expect("first body rule");
15062            let last_body = root.child_rules(1).next_back().expect("last body rule");
15063            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
15064            assert_eq!(
15065                last_body.stop_id().expect("last body stop").index(),
15066                REPETITIONS - 1
15067            );
15068
15069            let stats = parser.recognition_arena_stats();
15070            assert_eq!(
15071                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
15072                (REPETITIONS, REPETITIONS, 0)
15073            );
15074            assert_eq!(
15075                (stats.total_links, stats.live_links, stats.dead_links),
15076                (REPETITIONS, REPETITIONS, 0)
15077            );
15078            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
15079            assert_eq!(
15080                parser.recognition_arena.deferred_nodes.len(),
15081                REPETITIONS * 2 - 1
15082            );
15083            assert_eq!(parser.number_of_syntax_errors(), 0);
15084        }
15085    }
15086
15087    #[test]
15088    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
15089        let key = |state_number| FastRecognizeKey {
15090            state_number,
15091            stop_state: 10,
15092            index: state_number,
15093            rule_start_index: 0,
15094            decision_start_index: None,
15095            precedence: 0,
15096            recovery_symbols_id: 0,
15097            recovery_state: None,
15098        };
15099
15100        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15101        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
15102            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
15103        }
15104        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
15105        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
15106
15107        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15108        let repeated = key(1);
15109        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
15110            assert!(promote.clean_memo_enabled_for_key(&repeated));
15111        }
15112        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
15113
15114        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
15115            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
15116        }
15117        assert!(sparse.clean_memo_enabled_for_key(&repeated));
15118        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
15119        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
15120            assert!(sparse.clean_memo_enabled_for_key(&repeated));
15121        }
15122        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
15123    }
15124
15125    #[test]
15126    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
15127        assert_eq!(
15128            fast_recognize_memo_capacity(0),
15129            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15130        );
15131        assert_eq!(
15132            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
15133            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15134        );
15135        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
15136        assert_eq!(
15137            fast_recognize_memo_capacity(usize::MAX),
15138            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
15139        );
15140    }
15141
15142    #[test]
15143    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
15144        let mut scratch = FastRecognizeTopScratch::default();
15145        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15146        let retained_capacity = scratch.memo.capacity();
15147        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15148        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15149
15150        let larger_capacity = retained_capacity + 1;
15151        scratch.prepare(larger_capacity);
15152        let grown_capacity = scratch.memo.capacity();
15153        assert!(grown_capacity >= larger_capacity);
15154        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15155
15156        scratch.memo.insert(
15157            FastRecognizeKey {
15158                state_number: 0,
15159                stop_state: 0,
15160                index: 0,
15161                rule_start_index: 0,
15162                decision_start_index: None,
15163                precedence: 0,
15164                recovery_symbols_id: 0,
15165                recovery_state: None,
15166            },
15167            Rc::from([FastRecognizeOutcome {
15168                index: 0,
15169                consumed_eof: false,
15170                diagnostics: DiagnosticSeqId::EMPTY,
15171                deferred_nodes: FastDeferredNodeId::EMPTY,
15172                nodes: NodeSeqId::EMPTY,
15173            }]),
15174        );
15175        scratch.release_oversized_memo();
15176        assert!(scratch.memo.is_empty());
15177        assert_eq!(scratch.memo.capacity(), grown_capacity);
15178
15179        scratch
15180            .memo
15181            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
15182        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15183
15184        scratch.release_oversized_memo();
15185        assert!(scratch.memo.is_empty());
15186        assert_eq!(scratch.memo.capacity(), 0);
15187    }
15188
15189    #[test]
15190    fn clean_empty_multi_alt_outcomes_are_memoized() {
15191        let mut atn = ParserAtnBuilder::new(2);
15192        assert_eq!(
15193            atn.add_state(AtnStateKind::RuleStart, Some(0))
15194                .expect("state")
15195                .index(),
15196            0
15197        );
15198        assert_eq!(
15199            atn.add_state(AtnStateKind::BlockStart, Some(0))
15200                .expect("state")
15201                .index(),
15202            1
15203        );
15204        assert_eq!(
15205            atn.add_state(AtnStateKind::RuleStop, Some(0))
15206                .expect("state")
15207                .index(),
15208            2
15209        );
15210        atn.set_rule_to_start_state(vec![0])
15211            .expect("rule start states");
15212        atn.set_rule_to_stop_state(vec![2])
15213            .expect("rule stop states");
15214        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15215            .expect("transition");
15216        atn.add_transition(
15217            1,
15218            ParserTransitionSpec::Atom {
15219                target: 2,
15220                label: 1,
15221            },
15222        )
15223        .expect("transition");
15224        atn.add_transition(
15225            1,
15226            ParserTransitionSpec::Atom {
15227                target: 2,
15228                label: 2,
15229            },
15230        )
15231        .expect("transition");
15232        let atn = finish_atn(atn);
15233
15234        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15235        parser.fast_recovery_enabled = false;
15236        let mut visiting = FxHashSet::default();
15237        let mut memo = FxHashMap::default();
15238        let mut expected = ExpectedTokens::default();
15239        let outcomes = parser.recognize_state_fast(
15240            &atn,
15241            FastRecognizeRequest {
15242                state_number: 1,
15243                stop_state: 2,
15244                index: 0,
15245                rule_start_index: 0,
15246                decision_start_index: None,
15247                precedence: 0,
15248                depth: 0,
15249                recovery_symbols: parser.empty_recovery_symbols(),
15250                recovery_state: None,
15251            },
15252            FastRecognizeScratch {
15253                predicate_context: None,
15254                visiting: &mut visiting,
15255                memo: &mut memo,
15256                expected: &mut expected,
15257            },
15258        );
15259
15260        assert!(outcomes.is_empty());
15261        assert_eq!(memo.len(), 1);
15262        assert!(memo.values().next().expect("memo entry").is_empty());
15263
15264        parser.clean_memo_mode = CleanMemoMode::Sparse;
15265        visiting.clear();
15266        memo.clear();
15267        expected = ExpectedTokens::default();
15268        let sparse_outcomes = parser.recognize_state_fast(
15269            &atn,
15270            FastRecognizeRequest {
15271                state_number: 1,
15272                stop_state: 2,
15273                index: 0,
15274                rule_start_index: 0,
15275                decision_start_index: None,
15276                precedence: 0,
15277                depth: 0,
15278                recovery_symbols: parser.empty_recovery_symbols(),
15279                recovery_state: None,
15280            },
15281            FastRecognizeScratch {
15282                predicate_context: None,
15283                visiting: &mut visiting,
15284                memo: &mut memo,
15285                expected: &mut expected,
15286            },
15287        );
15288
15289        assert!(sparse_outcomes.is_empty());
15290        assert!(memo.is_empty());
15291    }
15292
15293    #[test]
15294    fn wildcard_matches_non_eof_only() {
15295        let mut parser = mini_parser(vec![
15296            TestToken::new(1).with_text("x"),
15297            TestToken::eof("parser-test", 1, 1, 1),
15298        ]);
15299        let matched = parser.match_wildcard().expect("wildcard");
15300        assert_eq!(parser.node(matched).text(), "x");
15301        assert!(parser.match_wildcard().is_err());
15302    }
15303
15304    #[test]
15305    fn add_parse_child_records_match_even_without_tree_building() {
15306        // `sync_decision`'s "is the current context empty" flag must reflect real
15307        // matches, not parse-tree children: when `build_parse_trees(false)`,
15308        // `children` stays empty but `has_matched_child` must still flip so nested
15309        // recovery does not wrongly suppress single-token deletion.
15310        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15311        let token = TestToken::new(1).with_text("x");
15312
15313        parser.set_build_parse_trees(false);
15314        let mut ctx = ParserRuleContext::new(0, 0);
15315        assert!(!ctx.has_matched_child());
15316        let child = parser.terminal_tree(token.id);
15317        parser.add_parse_child(&mut ctx, child);
15318        // Tree building is off, so no child is stored...
15319        assert_eq!(ctx.child_count(), 0);
15320        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15321        // ...but the match is recorded, so the context is no longer "empty".
15322        assert!(ctx.has_matched_child());
15323
15324        // With tree building on, the child is stored and the match is recorded.
15325        parser.set_build_parse_trees(true);
15326        let mut ctx = ParserRuleContext::new(0, 0);
15327        let child = parser.terminal_tree(token.id);
15328        parser.add_parse_child(&mut ctx, child);
15329        assert_eq!(ctx.child_count(), 1);
15330        assert!(ctx.has_matched_child());
15331    }
15332
15333    #[test]
15334    fn disabled_tree_building_does_not_grow_flat_storage() {
15335        let mut parser = mini_parser(vec![
15336            TestToken::new(1).with_text("x"),
15337            TestToken::new(1).with_text("y"),
15338            TestToken::eof("parser-test", 2, 1, 2),
15339        ]);
15340        parser.set_build_parse_trees(false);
15341        let mut context = ParserRuleContext::new(0, -1);
15342
15343        for _ in 0..2 {
15344            let child = parser.match_token(1).expect("token should match");
15345            parser.add_parse_child(&mut context, child);
15346        }
15347        let current = parser.input.lt_id(1).expect("EOF token");
15348        let error = parser.error_tree(current);
15349        parser.add_parse_child(&mut context, error);
15350        let root = parser.rule_node(context);
15351
15352        assert_eq!(
15353            parser.parse_tree_storage().stats(),
15354            ParseTreeStats::default()
15355        );
15356        assert!(
15357            parser
15358                .parse_tree_storage()
15359                .node(parser.token_store(), root)
15360                .is_none(),
15361            "the no-tree sentinel must not resolve to stored data"
15362        );
15363    }
15364
15365    #[test]
15366    fn disabled_tree_building_skips_recognition_rule_node_storage() {
15367        let atn = ordinary_star_loop_atn();
15368        let mut parser = mini_parser(repeated_x_tokens(3));
15369        parser.set_build_parse_trees(false);
15370
15371        parser
15372            .parse_atn_rule(&atn, 0)
15373            .expect("ordinary repetition should parse without a tree");
15374
15375        assert_eq!(parser.input.index(), 3);
15376        assert!(parser.recognition_arena.nodes.is_empty());
15377        assert!(parser.recognition_arena.seq_links.is_empty());
15378        assert!(parser.recognition_arena.deferred_nodes.is_empty());
15379        assert!(parser.recognition_arena.deferred_rules.is_empty());
15380        assert!(!parser.fast_token_nodes_enabled);
15381        assert!(parser.fast_recognize_scratch.memo.is_empty());
15382    }
15383
15384    #[test]
15385    fn parser_interprets_simple_atn_rule() {
15386        let atn = token_then_eof_atn();
15387        let mut parser = mini_parser(vec![
15388            TestToken::new(1).with_text("x"),
15389            TestToken::eof("parser-test", 1, 1, 1),
15390        ]);
15391
15392        let tree = parser
15393            .parse_atn_rule(&atn, 0)
15394            .expect("artificial parser rule should parse");
15395        assert_eq!(parser.node(tree).text(), "x<EOF>");
15396        assert_eq!(parser.number_of_syntax_errors(), 0);
15397        assert_eq!(
15398            parser
15399                .node(tree)
15400                .first_rule_stop(0)
15401                .expect("rule should stop at EOF")
15402                .token_type(),
15403            TOKEN_EOF
15404        );
15405
15406        let mut parser = mini_parser(vec![
15407            TestToken::new(1).with_text("x"),
15408            TestToken::eof("parser-test", 1, 1, 1),
15409        ]);
15410        let (tree, actions) = parser
15411            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15412            .expect("runtime-option parser rule should parse");
15413        assert!(actions.is_empty());
15414        assert_eq!(
15415            parser
15416                .node(tree)
15417                .first_rule_stop(0)
15418                .expect("rule should stop at EOF")
15419                .token_type(),
15420            TOKEN_EOF
15421        );
15422    }
15423
15424    #[test]
15425    fn runtime_options_default_ignores_noop_action_transitions() {
15426        let atn = noop_action_then_token_then_eof_atn();
15427        let mut parser = mini_parser(vec![
15428            TestToken::new(1).with_text("x"),
15429            TestToken::eof("parser-test", 1, 1, 1),
15430        ]);
15431
15432        let (tree, actions) = parser
15433            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15434            .expect("no-op parser action should not force action replay");
15435
15436        assert_eq!(parser.node(tree).text(), "x<EOF>");
15437        assert!(
15438            actions.is_empty(),
15439            "action_index=None transitions are ANTLR metadata, not replay actions"
15440        );
15441        assert_eq!(parser.number_of_syntax_errors(), 0);
15442    }
15443
15444    #[test]
15445    fn parser_exposes_buffered_token_stream_after_parse() {
15446        let atn = token_then_eof_atn();
15447        let mut parser = mini_parser(vec![
15448            TestToken::new(1).with_text("x"),
15449            TestToken::eof("parser-test", 1, 1, 1),
15450        ]);
15451
15452        let tree = parser
15453            .parse_atn_rule(&atn, 0)
15454            .expect("artificial parser rule should parse");
15455        assert_eq!(parser.node(tree).text(), "x<EOF>");
15456
15457        let stream = parser.token_stream();
15458        let source_index_after_parse = stream.token_source().index;
15459        let buffered = stream.tokens().collect::<Vec<_>>();
15460        assert_eq!(buffered.len(), 2);
15461        assert_eq!(buffered[0].text(), "x");
15462        assert_eq!(buffered[0].token_id().index(), 0);
15463        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
15464        assert_eq!(stream.token_source().index, source_index_after_parse);
15465        drop(buffered);
15466
15467        let stream = parser.into_token_stream();
15468        assert_eq!(stream.token_source().index, source_index_after_parse);
15469        assert_eq!(stream.tokens().next().expect("first token").text(), "x");
15470        assert_eq!(
15471            stream.tokens().nth(1).expect("EOF token").token_type(),
15472            TOKEN_EOF
15473        );
15474    }
15475
15476    #[test]
15477    fn parser_syntax_error_count_tracks_interpreted_recovery() {
15478        let atn = token_then_eof_atn();
15479        let mut parser = mini_parser(vec![
15480            TestToken::new(1).with_text("x"),
15481            TestToken::new(2).with_text("y"),
15482            TestToken::eof("parser-test", 2, 1, 2),
15483        ]);
15484
15485        let tree = parser
15486            .parse_atn_rule(&atn, 0)
15487            .expect("invalid token should recover into an error node");
15488
15489        assert_eq!(parser.number_of_syntax_errors(), 1);
15490        assert_eq!(
15491            parser
15492                .node(tree)
15493                .first_error_token()
15494                .expect("recovery should embed an error token")
15495                .text(),
15496            "y"
15497        );
15498    }
15499
15500    #[test]
15501    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
15502        let atn = token_then_eof_atn();
15503        let mut parser = mini_parser(vec![
15504            TestToken::new(2).with_text("y"),
15505            TestToken::eof("parser-test", 1, 1, 1),
15506        ]);
15507
15508        let error = parser
15509            .parse_atn_rule(&atn, 0)
15510            .expect_err("start-rule mismatch should remain a parser error");
15511
15512        assert_eq!(parser.number_of_syntax_errors(), 1);
15513        assert!(matches!(error, AntlrError::ParserError { .. }));
15514    }
15515
15516    #[test]
15517    fn adaptive_direct_rule_uses_simulator_decision() {
15518        let atn = two_alt_decision_atn();
15519        let mut simulator = ParserAtnSimulator::new(&atn);
15520        let mut parser = mini_parser(vec![
15521            TestToken::new(2).with_text("y"),
15522            TestToken::eof("parser-test", 1, 1, 1),
15523        ]);
15524
15525        let tree = parser
15526            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15527            .expect("direct adaptive rule should parse");
15528
15529        assert_eq!(parser.node(tree).text(), "y");
15530        assert_eq!(parser.input.index(), 1);
15531    }
15532
15533    #[test]
15534    fn adaptive_direct_rule_restores_input_on_fallback() {
15535        let atn = predicate_after_token_atn();
15536        let mut simulator = ParserAtnSimulator::new(&atn);
15537        let mut parser = mini_parser(vec![
15538            TestToken::new(1).with_text("x"),
15539            TestToken::new(2).with_text("y"),
15540            TestToken::eof("parser-test", 2, 1, 2),
15541        ]);
15542
15543        let tree = parser
15544            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15545            .expect("fallback recognizer should parse");
15546
15547        assert_eq!(parser.node(tree).text(), "xy");
15548        assert_eq!(parser.input.index(), 2);
15549        let stats = parser.parse_tree_storage().stats();
15550        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
15551        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
15552        assert_eq!(stats.scratch_links, 0);
15553    }
15554
15555    #[test]
15556    fn unknown_predicate_policy_defaults_to_assume_true() {
15557        let atn = predicate_after_token_atn();
15558        let mut parser = mini_parser(vec![
15559            TestToken::new(1).with_text("x"),
15560            TestToken::new(2).with_text("y"),
15561            TestToken::eof("parser-test", 2, 1, 2),
15562        ]);
15563
15564        let (tree, _) = parser
15565            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15566            .expect("unknown predicate should pass under the default policy");
15567
15568        assert_eq!(parser.node(tree).text(), "xy");
15569        assert_eq!(parser.number_of_syntax_errors(), 0);
15570    }
15571
15572    #[test]
15573    fn predicate_gated_same_lookahead_uses_viable_alternative() {
15574        let atn = predicate_gated_same_lookahead_atn([0, 1]);
15575        let mut parser = mini_parser(vec![
15576            TestToken::new(1).with_text("x"),
15577            TestToken::eof("parser-test", 1, 1, 1),
15578        ]);
15579
15580        let (tree, _) = parser
15581            .parse_atn_rule_with_runtime_options(
15582                &atn,
15583                0,
15584                ParserRuntimeOptions {
15585                    predicates: &[
15586                        (0, 0, ParserPredicate::False),
15587                        (0, 1, ParserPredicate::True),
15588                    ],
15589                    ..ParserRuntimeOptions::default()
15590                },
15591            )
15592            .expect("the second predicate-gated alternative should match");
15593
15594        assert_eq!(parser.node(tree).text(), "x<EOF>");
15595        assert_eq!(parser.number_of_syntax_errors(), 0);
15596        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
15597        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
15598    }
15599
15600    #[test]
15601    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
15602        // A generated parent may record an unknown-predicate coordinate, then
15603        // descend into an interpreted child. The child's interpreter entry must
15604        // not wipe the parent's recorded hit before the top-level surfaces it.
15605        let atn = token_then_eof_atn();
15606        let mut parser = mini_parser(vec![
15607            TestToken::new(1).with_text("x"),
15608            TestToken::eof("parser-test", 1, 1, 1),
15609        ]);
15610
15611        // Simulate the parent having recorded a fail-loud coordinate.
15612        parser.unknown_predicate_hits.push((7, 3));
15613
15614        // Run an interpreted child parse that records no coordinate of its own.
15615        parser
15616            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15617            .expect("child rule parses");
15618
15619        // The parent's coordinate must still be present for the top-level entry.
15620        let error = parser
15621            .take_unknown_semantic_error()
15622            .expect("parent's recorded coordinate must survive the nested interpreted parse");
15623        let AntlrError::Unsupported(message) = error else {
15624            panic!("expected AntlrError::Unsupported, got {error:?}");
15625        };
15626        assert!(message.contains("pred_index=3"), "message: {message}");
15627    }
15628
15629    #[test]
15630    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
15631        let atn = predicate_after_token_atn();
15632        let mut parser = mini_parser(vec![
15633            TestToken::new(1).with_text("x"),
15634            TestToken::new(2).with_text("y"),
15635            TestToken::eof("parser-test", 2, 1, 2),
15636        ]);
15637
15638        let result = parser.parse_atn_rule_with_runtime_options(
15639            &atn,
15640            0,
15641            ParserRuntimeOptions {
15642                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
15643                ..ParserRuntimeOptions::default()
15644            },
15645        );
15646
15647        assert!(
15648            result.is_err(),
15649            "the only path is predicate-guarded, so assume-false must fail the parse"
15650        );
15651    }
15652
15653    #[test]
15654    fn predicate_failure_message_keeps_semantic_recovery_path() {
15655        let atn = predicate_after_token_atn();
15656        let mut parser = mini_parser(vec![
15657            TestToken::new(1).with_text("x"),
15658            TestToken::new(2).with_text("y"),
15659            TestToken::eof("parser-test", 2, 1, 2),
15660        ]);
15661
15662        let (tree, _) = parser
15663            .parse_atn_rule_with_runtime_options(
15664                &atn,
15665                0,
15666                ParserRuntimeOptions {
15667                    predicates: &[(
15668                        0,
15669                        0,
15670                        ParserPredicate::FalseWithMessage {
15671                            message: "predicate rejected input",
15672                        },
15673                    )],
15674                    ..ParserRuntimeOptions::default()
15675                },
15676            )
15677            .expect("failure-message predicates recover through the semantic interpreter");
15678
15679        assert_eq!(parser.node(tree).text(), "xy");
15680        assert_eq!(parser.number_of_syntax_errors(), 1);
15681        assert!(
15682            parser.fast_predicate_cache.is_empty(),
15683            "failure-message predicates need the semantic interpreter's recovery outcome"
15684        );
15685    }
15686
15687    #[test]
15688    fn unknown_predicate_policy_error_names_the_coordinate() {
15689        let atn = predicate_after_token_atn();
15690        let mut parser = mini_parser(vec![
15691            TestToken::new(1).with_text("x"),
15692            TestToken::new(2).with_text("y"),
15693            TestToken::eof("parser-test", 2, 1, 2),
15694        ]);
15695
15696        let error = parser
15697            .parse_atn_rule_with_runtime_options(
15698                &atn,
15699                0,
15700                ParserRuntimeOptions {
15701                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15702                    ..ParserRuntimeOptions::default()
15703                },
15704            )
15705            .expect_err("evaluating an unknown predicate under Error policy must fail");
15706
15707        let AntlrError::Unsupported(message) = error else {
15708            panic!("expected AntlrError::Unsupported, got {error:?}");
15709        };
15710        assert!(
15711            message.contains("unsupported semantic predicate"),
15712            "message should name the failure class: {message}"
15713        );
15714        assert!(
15715            message.contains("pred_index=0"),
15716            "message should carry the coordinate: {message}"
15717        );
15718    }
15719
15720    #[test]
15721    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
15722        // A parser reused after a fail-loud parse must not carry the old
15723        // coordinates into a later parse. The fail-loud return keeps the hits
15724        // (so a generated parent can surface a recovered child's coordinate),
15725        // and the next parse's entry stashes/replaces them, so a subsequent
15726        // clean parse surfaces no stale error.
15727        let atn = predicate_after_token_atn();
15728        let mut parser = mini_parser(vec![
15729            TestToken::new(1).with_text("x"),
15730            TestToken::new(2).with_text("y"),
15731            TestToken::eof("parser-test", 2, 1, 2),
15732        ]);
15733
15734        parser
15735            .parse_atn_rule_with_runtime_options(
15736                &atn,
15737                0,
15738                ParserRuntimeOptions {
15739                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15740                    ..ParserRuntimeOptions::default()
15741                },
15742            )
15743            .expect_err("first parse fails loud under the Error policy");
15744
15745        // The failed parse kept its coordinate on the parser (so a generated
15746        // parent could surface a recovered child). A top-level reuse resets the
15747        // hits — generated parsers call `reset_unknown_semantic_hits` at their
15748        // public entry; direct interpreter-API callers do the same.
15749        parser.reset_unknown_semantic_hits();
15750        assert!(
15751            parser.take_unknown_semantic_error().is_none(),
15752            "reset must drop stale unknown-predicate coordinates before a reused parse"
15753        );
15754    }
15755
15756    #[derive(Debug, Default)]
15757    struct RecordingHooks {
15758        predicates: Vec<(usize, usize, usize, Option<String>)>,
15759        actions: Vec<(usize, String, Option<String>)>,
15760        action_trees: Vec<Option<String>>,
15761    }
15762
15763    impl SemanticHooks for RecordingHooks {
15764        fn sempred<S>(
15765            &mut self,
15766            ctx: &mut ParserSemCtx<'_, S>,
15767            rule_index: usize,
15768            pred_index: usize,
15769        ) -> Option<bool>
15770        where
15771            S: TokenSource,
15772        {
15773            self.predicates.push((
15774                ctx.input_index(),
15775                rule_index,
15776                pred_index,
15777                ctx.token_text(1).map(|token| token.text().to_owned()),
15778            ));
15779            Some(true)
15780        }
15781
15782        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
15783        where
15784            S: TokenSource,
15785        {
15786            self.actions.push((
15787                action.source_state(),
15788                ctx.action_text(),
15789                ctx.rule_name().map(str::to_owned),
15790            ));
15791            self.action_trees.push(ctx.tree().map(Node::text));
15792            true
15793        }
15794    }
15795
15796    #[derive(Debug, Default)]
15797    struct RejectingPredicateHooks {
15798        predicates: Vec<(usize, usize, usize, Option<String>)>,
15799    }
15800
15801    impl SemanticHooks for RejectingPredicateHooks {
15802        fn sempred<S>(
15803            &mut self,
15804            ctx: &mut ParserSemCtx<'_, S>,
15805            rule_index: usize,
15806            pred_index: usize,
15807        ) -> Option<bool>
15808        where
15809            S: TokenSource,
15810        {
15811            self.predicates.push((
15812                ctx.input_index(),
15813                rule_index,
15814                pred_index,
15815                ctx.token_text(1).map(|token| token.text().to_owned()),
15816            ));
15817            Some(false)
15818        }
15819    }
15820
15821    #[test]
15822    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
15823        let atn = predicate_gated_same_lookahead_atn([0, 0]);
15824        let mut parser = mini_parser_with_hooks(
15825            vec![
15826                TestToken::new(1).with_text("x"),
15827                TestToken::eof("parser-test", 1, 1, 1),
15828            ],
15829            RecordingHooks::default(),
15830        );
15831
15832        let (tree, _) = parser
15833            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15834            .expect("both alternatives share one replay-safe predicate result");
15835
15836        assert_eq!(parser.node(tree).text(), "x<EOF>");
15837        assert_eq!(
15838            parser.semantic_hooks.predicates,
15839            vec![(0, 0, 0, Some("x".to_owned()))]
15840        );
15841        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
15842    }
15843
15844    #[test]
15845    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
15846        let atn = predicate_after_token_atn();
15847        let mut parser = mini_parser_with_hooks(
15848            vec![
15849                TestToken::new(1).with_text("x"),
15850                TestToken::new(2).with_text("y"),
15851                TestToken::eof("parser-test", 2, 1, 2),
15852            ],
15853            RecordingHooks::default(),
15854        );
15855
15856        let (tree, _) = parser
15857            .parse_atn_rule_with_runtime_options(
15858                &atn,
15859                0,
15860                ParserRuntimeOptions {
15861                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15862                    ..ParserRuntimeOptions::default()
15863                },
15864            )
15865            .expect("hook supplies the missing predicate result");
15866
15867        assert_eq!(parser.node(tree).text(), "xy");
15868        assert_eq!(
15869            parser.semantic_hooks.predicates,
15870            vec![(1, 0, 0, Some("y".to_owned()))]
15871        );
15872        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
15873    }
15874
15875    #[test]
15876    fn runtime_options_default_preserves_semantic_hook_predicates() {
15877        let atn = predicate_after_token_atn();
15878        let mut parser = mini_parser_with_hooks(
15879            vec![
15880                TestToken::new(1).with_text("x"),
15881                TestToken::new(2).with_text("y"),
15882                TestToken::eof("parser-test", 2, 1, 2),
15883            ],
15884            RejectingPredicateHooks::default(),
15885        );
15886
15887        let result =
15888            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
15889
15890        assert!(
15891            result.is_err(),
15892            "default runtime options must not bypass semantic hooks for predicate ATNs"
15893        );
15894        assert_eq!(
15895            parser.semantic_hooks.predicates,
15896            vec![(1, 0, 0, Some("y".to_owned()))]
15897        );
15898        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
15899    }
15900
15901    #[test]
15902    fn semantic_hook_handles_committed_parser_action() {
15903        let atn = token_then_eof_atn();
15904        let mut parser = mini_parser_with_hooks(
15905            vec![
15906                TestToken::new(1).with_text("x"),
15907                TestToken::eof("parser-test", 1, 1, 1),
15908            ],
15909            RecordingHooks::default(),
15910        );
15911        let (tree, _) = parser
15912            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15913            .expect("rule parses before action hook is tested");
15914
15915        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
15916        assert_eq!(
15917            parser.semantic_hooks.actions,
15918            vec![(42, "x".to_owned(), Some("s".to_owned()))]
15919        );
15920        assert_eq!(
15921            parser.semantic_hooks.action_trees,
15922            [Some("x<EOF>".to_owned())]
15923        );
15924    }
15925
15926    #[test]
15927    fn unhandled_committed_action_fails_loud_under_error_policy() {
15928        // An action offered to the hook that no hook handles (returns false)
15929        // must be recorded and surfaced as `AntlrError::Unsupported` under the
15930        // Error policy, so a `hook`-disposed action is not silently dropped.
15931        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
15932        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
15933        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
15934
15935        // DecliningHooks::action returns false (unhandled).
15936        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
15937
15938        let error = parser
15939            .take_unknown_semantic_error()
15940            .expect("an unhandled committed action under Error policy must fail loud");
15941        let AntlrError::Unsupported(message) = error else {
15942            panic!("expected AntlrError::Unsupported, got {error:?}");
15943        };
15944        assert!(
15945            message.contains("unhandled semantic action") && message.contains("state=42"),
15946            "message should name the dropped action coordinate: {message}"
15947        );
15948
15949        // Under the default (assume-true) policy the same miss is not recorded.
15950        let mut lenient =
15951            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
15952        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
15953        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
15954        assert!(lenient.take_unknown_semantic_error().is_none());
15955    }
15956
15957    #[test]
15958    fn translated_predicate_is_unaffected_by_error_policy() {
15959        let atn = predicate_after_token_atn();
15960        let mut parser = mini_parser(vec![
15961            TestToken::new(1).with_text("x"),
15962            TestToken::new(2).with_text("y"),
15963            TestToken::eof("parser-test", 2, 1, 2),
15964        ]);
15965
15966        let (tree, _) = parser
15967            .parse_atn_rule_with_runtime_options(
15968                &atn,
15969                0,
15970                ParserRuntimeOptions {
15971                    predicates: &[(0, 0, ParserPredicate::True)],
15972                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
15973                    ..ParserRuntimeOptions::default()
15974                },
15975            )
15976            .expect("a predicate covered by the table is not an unknown coordinate");
15977
15978        assert_eq!(parser.node(tree).text(), "xy");
15979    }
15980
15981    /// Hooks that decline (`None`) must fall through to the configured policy
15982    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
15983    /// legacy table path. Regression for the `unwrap_or(false)` that silently
15984    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
15985    fn hook_predicate_semantics() -> ParserSemantics {
15986        let mut ir = SemIr::new();
15987        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
15988        ParserSemantics {
15989            ir,
15990            predicates: vec![ParserSemanticPredicate {
15991                rule_index: 0,
15992                pred_index: 0,
15993                expr,
15994                failure_message: None,
15995            }],
15996            actions: Vec::new(),
15997        }
15998    }
15999
16000    #[derive(Debug, Default)]
16001    struct DecliningHooks;
16002
16003    impl SemanticHooks for DecliningHooks {}
16004
16005    #[test]
16006    fn semir_hook_none_falls_through_to_assume_true() {
16007        let atn = predicate_after_token_atn();
16008        let semantics = hook_predicate_semantics();
16009        let mut parser = mini_parser_with_hooks(
16010            vec![
16011                TestToken::new(1).with_text("x"),
16012                TestToken::new(2).with_text("y"),
16013                TestToken::eof("parser-test", 2, 1, 2),
16014            ],
16015            DecliningHooks,
16016        );
16017
16018        let (tree, _) = parser
16019            .parse_atn_rule_with_runtime_options(
16020                &atn,
16021                0,
16022                ParserRuntimeOptions {
16023                    semantics: Some(&semantics),
16024                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
16025                    ..ParserRuntimeOptions::default()
16026                },
16027            )
16028            .expect("a declined SemIR hook must pass under assume-true");
16029
16030        assert_eq!(parser.node(tree).text(), "xy");
16031    }
16032
16033    #[test]
16034    fn semir_hook_none_falls_through_to_assume_false() {
16035        let atn = predicate_after_token_atn();
16036        let semantics = hook_predicate_semantics();
16037        let mut parser = mini_parser_with_hooks(
16038            vec![
16039                TestToken::new(1).with_text("x"),
16040                TestToken::new(2).with_text("y"),
16041                TestToken::eof("parser-test", 2, 1, 2),
16042            ],
16043            DecliningHooks,
16044        );
16045
16046        let result = parser.parse_atn_rule_with_runtime_options(
16047            &atn,
16048            0,
16049            ParserRuntimeOptions {
16050                semantics: Some(&semantics),
16051                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16052                ..ParserRuntimeOptions::default()
16053            },
16054        );
16055
16056        assert!(
16057            result.is_err(),
16058            "a declined SemIR hook must fail the only guarded path under assume-false"
16059        );
16060    }
16061
16062    #[test]
16063    fn semir_hook_none_records_coordinate_under_error_policy() {
16064        let atn = predicate_after_token_atn();
16065        let semantics = hook_predicate_semantics();
16066        let mut parser = mini_parser_with_hooks(
16067            vec![
16068                TestToken::new(1).with_text("x"),
16069                TestToken::new(2).with_text("y"),
16070                TestToken::eof("parser-test", 2, 1, 2),
16071            ],
16072            DecliningHooks,
16073        );
16074
16075        let error = parser
16076            .parse_atn_rule_with_runtime_options(
16077                &atn,
16078                0,
16079                ParserRuntimeOptions {
16080                    semantics: Some(&semantics),
16081                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16082                    ..ParserRuntimeOptions::default()
16083                },
16084            )
16085            .expect_err("a declined SemIR hook under Error policy must fail the parse");
16086
16087        let AntlrError::Unsupported(message) = error else {
16088            panic!("expected AntlrError::Unsupported, got {error:?}");
16089        };
16090        assert!(
16091            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
16092            "message should name the unresolved coordinate: {message}"
16093        );
16094    }
16095
16096    #[test]
16097    fn generated_direct_predicate_honors_installed_policy() {
16098        // The generated recursive-descent path calls
16099        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
16100        // going through `ParserRuntimeOptions`, so the policy must be installed
16101        // via `set_unknown_predicate_policy` (as the generated constructor now
16102        // does). A declining hook must then honor it rather than the default.
16103        let semantics = hook_predicate_semantics();
16104        let context = ParserRuleContext::new(0, -1);
16105
16106        let mut assume_true =
16107            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16108        assert!(
16109            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
16110                &semantics, 0, 0, &context, 0
16111            ),
16112            "default AssumeTrue accepts a declined hook"
16113        );
16114        assert!(assume_true.take_unknown_semantic_error().is_none());
16115
16116        let mut error_policy =
16117            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16118        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16119        assert!(
16120            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
16121                &semantics, 0, 0, &context, 0
16122            ),
16123            "Error policy rejects a declined hook on the generated-direct path"
16124        );
16125        let error = error_policy
16126            .take_unknown_semantic_error()
16127            .expect("Error policy records the unresolved coordinate for the generated path");
16128        let AntlrError::Unsupported(message) = error else {
16129            panic!("expected AntlrError::Unsupported, got {error:?}");
16130        };
16131        assert!(message.contains("pred_index=0"), "message: {message}");
16132    }
16133
16134    #[test]
16135    fn parser_rule_start_skips_leading_hidden_tokens() {
16136        let atn = token_then_eof_atn();
16137        let mut parser = mini_parser(vec![
16138            TestToken::new(99)
16139                .with_text(" ")
16140                .with_channel(HIDDEN_CHANNEL),
16141            TestToken::new(1).with_text("x"),
16142            TestToken::eof("parser-test", 2, 1, 2),
16143        ]);
16144
16145        let tree = parser
16146            .parse_atn_rule(&atn, 0)
16147            .expect("artificial parser rule should parse");
16148        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
16149            panic!("rule node should be present");
16150        };
16151        assert_eq!(
16152            rule.start()
16153                .expect("rule should have a start token")
16154                .token_type(),
16155            1
16156        );
16157    }
16158
16159    #[test]
16160    fn parser_action_after_eof_stops_at_eof_token() {
16161        let atn = eof_then_action_atn();
16162        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16163
16164        let (_, actions) = parser
16165            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16166            .expect("EOF action rule should parse");
16167
16168        assert_eq!(actions.len(), 1);
16169        assert_eq!(actions[0].stop_index(), Some(0));
16170        assert_eq!(
16171            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
16172            ""
16173        );
16174    }
16175
16176    #[test]
16177    fn after_action_stop_uses_rule_context_stop_not_cursor() {
16178        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
16179        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
16180        // but the rule did not consume it. The @after stop must follow the rule
16181        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
16182        let mut id = TestToken::new(1).with_text("x");
16183        id.set_token_index(0);
16184        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
16185        eof.set_token_index(1);
16186        let mut parser = mini_parser(vec![id.clone(), eof]);
16187        // Advance the cursor onto EOF, as it would be after `a` matched ID.
16188        parser.consume();
16189        assert_eq!(parser.la(1), TOKEN_EOF);
16190
16191        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
16192        // exactly what finish_rule(consumed_eof = false) records.
16193        let mut ctx = ParserRuleContext::new(0, 0);
16194        parser.set_context_stop(
16195            &mut ctx,
16196            parser.token_id_at(0).expect("ID token should be buffered"),
16197        );
16198        let tree = parser.rule_node(ctx);
16199
16200        let current_index = parser.input.index();
16201        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
16202        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
16203        // ...but the tree-aware helper follows the rule context stop (ID).
16204        assert_eq!(
16205            parser.after_action_stop_index_for_tree(tree, current_index),
16206            Some(0)
16207        );
16208    }
16209
16210    #[test]
16211    fn after_action_start_uses_rule_context_start_not_cursor() {
16212        // A rule that begins after leading hidden-channel tokens: the rule context
16213        // start (set by `enter_rule`) is the first visible token, not the raw cursor
16214        // that may still point at the hidden prefix. The @after start must follow
16215        // the context start so `$start`/`$text` excludes the hidden prefix.
16216        let mut parser = mini_parser(vec![
16217            TestToken::new(9)
16218                .with_text(" ")
16219                .with_channel(HIDDEN_CHANNEL),
16220            TestToken::new(9)
16221                .with_text(" ")
16222                .with_channel(HIDDEN_CHANNEL),
16223            TestToken::new(1).with_text("x"),
16224            TestToken::eof("parser-test", 3, 1, 3),
16225        ]);
16226
16227        let mut ctx = ParserRuleContext::new(0, 0);
16228        parser.set_context_start(
16229            &mut ctx,
16230            parser.token_id_at(2).expect("ID token should be buffered"),
16231        );
16232        let tree = parser.rule_node(ctx);
16233
16234        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
16235        // ...but the tree-aware helper follows the rule context start (index 2).
16236        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
16237
16238        // With no rule start recorded, it falls back to the provided index.
16239        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
16240        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
16241    }
16242
16243    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
16244        FastRecognizeOutcome {
16245            index,
16246            consumed_eof,
16247            diagnostics: DiagnosticSeqId::EMPTY,
16248            deferred_nodes: FastDeferredNodeId::EMPTY,
16249            nodes: NodeSeqId(marker),
16250        }
16251    }
16252
16253    #[test]
16254    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
16255        let mut outcomes = vec![
16256            clean_fast_outcome(4, false, 0),
16257            clean_fast_outcome(2, false, 1),
16258            clean_fast_outcome(4, false, 2),
16259            clean_fast_outcome(4, true, 3),
16260            clean_fast_outcome(2, false, 4),
16261        ];
16262        let mut scratch = FastOutcomeDedupScratch::default();
16263
16264        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16265
16266        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
16267        assert_eq!(
16268            outcomes
16269                .iter()
16270                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
16271                .collect::<Vec<_>>(),
16272            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
16273        );
16274        assert!(scratch.dense_words.is_empty());
16275        assert!(scratch.sparse_keys.is_empty());
16276    }
16277
16278    #[test]
16279    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
16280        let mut scratch = FastOutcomeDedupScratch::default();
16281        let mut outcomes = (100..109)
16282            .flat_map(|index| {
16283                [
16284                    clean_fast_outcome(
16285                        index,
16286                        false,
16287                        u32::try_from(index).expect("test index fits in u32"),
16288                    ),
16289                    clean_fast_outcome(index, false, u32::MAX),
16290                ]
16291            })
16292            .collect();
16293
16294        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16295
16296        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16297        assert_eq!(outcomes.len(), 9);
16298        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
16299        let dense_capacity = scratch.dense_words.capacity();
16300
16301        let mut reused = (1_000..1_009)
16302            .map(|index| {
16303                clean_fast_outcome(
16304                    index,
16305                    false,
16306                    u32::try_from(index).expect("test index fits in u32"),
16307                )
16308            })
16309            .collect();
16310        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16311
16312        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16313        assert_eq!(reused.len(), 9);
16314        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
16315    }
16316
16317    #[test]
16318    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
16319        let mut scratch = FastOutcomeDedupScratch::default();
16320        let sparse_indexes = [
16321            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
16322        ];
16323        let mut outcomes = sparse_indexes
16324            .into_iter()
16325            .chain([400_000])
16326            .enumerate()
16327            .map(|(marker, index)| {
16328                clean_fast_outcome(
16329                    index,
16330                    false,
16331                    u32::try_from(marker).expect("test marker fits in u32"),
16332                )
16333            })
16334            .collect();
16335
16336        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16337
16338        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16339        assert_eq!(outcomes.len(), sparse_indexes.len());
16340        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
16341        let sparse_capacity = scratch.sparse_keys.capacity();
16342
16343        let mut reused = sparse_indexes
16344            .into_iter()
16345            .map(|index| {
16346                clean_fast_outcome(
16347                    index,
16348                    false,
16349                    u32::try_from(index).expect("test index fits in u32"),
16350                )
16351            })
16352            .collect();
16353        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16354
16355        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16356        assert_eq!(reused.len(), sparse_indexes.len());
16357        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
16358    }
16359
16360    #[test]
16361    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
16362        let mut scratch = FastOutcomeDedupScratch::default();
16363        scratch
16364            .sparse_keys
16365            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
16366        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16367        let mut outcomes = (0..9)
16368            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
16369            .collect();
16370
16371        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16372
16373        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16374        assert!(scratch.sparse_keys.is_empty());
16375        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16376    }
16377
16378    #[test]
16379    fn fast_outcome_selection_respects_sll_tie_order() {
16380        let mut arena = RecognitionArena::default();
16381        let first = FastRecognizeOutcome {
16382            index: 1,
16383            consumed_eof: false,
16384            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16385                line: 1,
16386                column: 0,
16387                message: "mismatched input 'x'".to_owned(),
16388            }]),
16389            deferred_nodes: FastDeferredNodeId::EMPTY,
16390            nodes: NodeSeqId::EMPTY,
16391        };
16392        let second = FastRecognizeOutcome {
16393            index: first.index,
16394            consumed_eof: first.consumed_eof,
16395            diagnostics: DiagnosticSeqId::EMPTY,
16396            deferred_nodes: FastDeferredNodeId::EMPTY,
16397            nodes: NodeSeqId::EMPTY,
16398        };
16399
16400        let selected = select_best_fast_outcome(
16401            [first, second].into_iter(),
16402            PredictionMode::Sll,
16403            None,
16404            |_| panic!("caller-follow token probe should not run"),
16405            &arena,
16406        )
16407        .expect("one outcome should be selected");
16408        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
16409        let eof_second = FastRecognizeOutcome {
16410            index: second.index,
16411            consumed_eof: true,
16412            diagnostics: DiagnosticSeqId::EMPTY,
16413            deferred_nodes: FastDeferredNodeId::EMPTY,
16414            nodes: NodeSeqId::EMPTY,
16415        };
16416        let selected = select_best_fast_outcome(
16417            [first, eof_second].into_iter(),
16418            PredictionMode::Sll,
16419            None,
16420            |_| panic!("caller-follow token probe should not run"),
16421            &arena,
16422        )
16423        .expect("one outcome should be selected");
16424        assert!(!selected.consumed_eof);
16425        let selected = select_best_fast_outcome(
16426            [first, second].into_iter(),
16427            PredictionMode::Ll,
16428            None,
16429            |_| panic!("caller-follow token probe should not run"),
16430            &arena,
16431        )
16432        .expect("one outcome should be selected");
16433        assert!(selected.diagnostics.is_empty());
16434    }
16435
16436    #[test]
16437    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
16438        let mut arena = RecognitionArena::default();
16439        let first = FastRecognizeOutcome {
16440            index: 3,
16441            consumed_eof: false,
16442            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16443                line: 1,
16444                column: 0,
16445                message: "mismatched input 'x' expecting 'a'".to_owned(),
16446            }]),
16447            deferred_nodes: FastDeferredNodeId::EMPTY,
16448            nodes: NodeSeqId::EMPTY,
16449        };
16450        let same_rank = FastRecognizeOutcome {
16451            index: first.index,
16452            consumed_eof: first.consumed_eof,
16453            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16454                line: 1,
16455                column: 0,
16456                message: "mismatched input 'x' expecting 'b'".to_owned(),
16457            }]),
16458            deferred_nodes: FastDeferredNodeId::EMPTY,
16459            nodes: NodeSeqId::EMPTY,
16460        };
16461        let better_rank = FastRecognizeOutcome {
16462            index: first.index,
16463            consumed_eof: first.consumed_eof,
16464            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16465                line: 1,
16466                column: 0,
16467                message: "missing 'a' at 'x'".to_owned(),
16468            }]),
16469            deferred_nodes: FastDeferredNodeId::EMPTY,
16470            nodes: NodeSeqId::EMPTY,
16471        };
16472        let mut outcomes = vec![first, same_rank, better_rank];
16473
16474        dedupe_fast_outcomes(&mut outcomes, &arena);
16475
16476        assert_eq!(outcomes.len(), 2);
16477        assert_eq!(
16478            arena
16479                .diagnostics(outcomes[0].diagnostics)
16480                .next()
16481                .expect("first diagnostic")
16482                .message,
16483            "mismatched input 'x' expecting 'a'"
16484        );
16485        assert_eq!(
16486            arena
16487                .diagnostics(outcomes[1].diagnostics)
16488                .next()
16489                .expect("second diagnostic")
16490                .message,
16491            "missing 'a' at 'x'"
16492        );
16493    }
16494
16495    #[test]
16496    fn fast_outcome_selection_prefers_generated_caller_follow() {
16497        let arena = RecognitionArena::default();
16498        let earlier = FastRecognizeOutcome {
16499            index: 7,
16500            consumed_eof: false,
16501            diagnostics: DiagnosticSeqId::EMPTY,
16502            deferred_nodes: FastDeferredNodeId::EMPTY,
16503            nodes: NodeSeqId::EMPTY,
16504        };
16505        let later = FastRecognizeOutcome {
16506            index: 8,
16507            consumed_eof: false,
16508            diagnostics: DiagnosticSeqId::EMPTY,
16509            deferred_nodes: FastDeferredNodeId::EMPTY,
16510            nodes: NodeSeqId::EMPTY,
16511        };
16512        let mut follow = TokenBitSet::default();
16513        follow.insert(5);
16514
16515        let selected = select_best_fast_outcome(
16516            [later, earlier].into_iter(),
16517            PredictionMode::Ll,
16518            Some(&follow),
16519            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
16520            &arena,
16521        )
16522        .expect("one outcome should be selected");
16523        assert_eq!(selected.index, 7);
16524
16525        let selected = select_best_fast_outcome(
16526            [later, earlier].into_iter(),
16527            PredictionMode::Ll,
16528            Some(&follow),
16529            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
16530            &arena,
16531        )
16532        .expect("one outcome should be selected");
16533        assert_eq!(selected.index, 8);
16534
16535        let indented_next_statement = FastRecognizeOutcome {
16536            index: 9,
16537            consumed_eof: false,
16538            diagnostics: DiagnosticSeqId::EMPTY,
16539            deferred_nodes: FastDeferredNodeId::EMPTY,
16540            nodes: NodeSeqId::EMPTY,
16541        };
16542        let selected = select_best_fast_outcome(
16543            [indented_next_statement, earlier].into_iter(),
16544            PredictionMode::Ll,
16545            Some(&follow),
16546            |index| {
16547                let is_boundary = index == 7;
16548                let is_boundary_gap = matches!(index, 7 | 8);
16549                (
16550                    if index == 7 { 5 } else { TOKEN_EOF },
16551                    is_boundary,
16552                    is_boundary_gap,
16553                )
16554            },
16555            &arena,
16556        )
16557        .expect("one outcome should be selected");
16558        assert_eq!(selected.index, 7);
16559
16560        let continuation = FastRecognizeOutcome {
16561            index: 10,
16562            consumed_eof: false,
16563            diagnostics: DiagnosticSeqId::EMPTY,
16564            deferred_nodes: FastDeferredNodeId::EMPTY,
16565            nodes: NodeSeqId::EMPTY,
16566        };
16567        let selected = select_best_fast_outcome(
16568            [continuation, earlier].into_iter(),
16569            PredictionMode::Ll,
16570            Some(&follow),
16571            |index| {
16572                let is_boundary = matches!(index, 7 | 9);
16573                (
16574                    if index == 7 { 5 } else { TOKEN_EOF },
16575                    is_boundary,
16576                    is_boundary,
16577                )
16578            },
16579            &arena,
16580        )
16581        .expect("one outcome should be selected");
16582        assert_eq!(selected.index, 10);
16583
16584        let selected = select_best_fast_outcome(
16585            [earlier, later].into_iter(),
16586            PredictionMode::Sll,
16587            Some(&follow),
16588            |_| panic!("caller-follow token probe should not run in SLL mode"),
16589            &arena,
16590        )
16591        .expect("one outcome should be selected");
16592        assert_eq!(selected.index, 8);
16593    }
16594
16595    #[test]
16596    fn caller_follow_boundary_text_requires_separator_shape() {
16597        assert!(is_caller_follow_boundary_text(";"));
16598        assert!(is_caller_follow_boundary_text("\n"));
16599        assert!(is_caller_follow_boundary_text("\r\n  "));
16600        assert!(is_caller_follow_boundary_text(";\n"));
16601        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
16602        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
16603        assert!(!is_caller_follow_boundary_text("identifier"));
16604        assert!(is_caller_follow_boundary_gap_text(" \t "));
16605        assert!(is_caller_follow_boundary_gap_text("\n  "));
16606        assert!(is_caller_follow_boundary_gap_text(";\t"));
16607        assert!(!is_caller_follow_boundary_gap_text(
16608            "\"\"\"line1\nline2\"\"\""
16609        ));
16610        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
16611    }
16612
16613    #[test]
16614    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
16615        let mut parser = mini_parser(vec![
16616            TestToken::new(5).with_text("\n"),
16617            TestToken::new(6)
16618                .with_text("// comment\n")
16619                .with_channel(HIDDEN_CHANNEL),
16620            TestToken::new(1).with_text("x"),
16621            TestToken::eof("parser-test", 1, 2, 0),
16622        ]);
16623
16624        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
16625        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
16626        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
16627    }
16628
16629    #[test]
16630    fn caller_follow_token_info_uses_stream_visible_channel() {
16631        let source = Source {
16632            tokens: vec![
16633                TestToken::new(5).with_text("\n").with_channel(2),
16634                TestToken::new(1).with_text("x").with_channel(2),
16635                TestToken::new(6)
16636                    .with_text("// comment\n")
16637                    .with_channel(HIDDEN_CHANNEL),
16638                TestToken::eof("parser-test", 1, 2, 0),
16639            ],
16640            index: 0,
16641        };
16642        let data = RecognizerData::new(
16643            "Mini.g4",
16644            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16645        );
16646        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
16647
16648        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
16649        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
16650        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
16651    }
16652
16653    #[test]
16654    fn reset_per_parse_caches_clears_state_expected_token_cache() {
16655        let atn = token_then_eof_atn();
16656        let mut parser = mini_parser(Vec::new());
16657
16658        let _ = parser.cached_state_expected_token_set(&atn, 0);
16659        assert!(!parser.state_expected_token_cache.is_empty());
16660
16661        parser.reset_per_parse_caches();
16662        assert!(parser.state_expected_token_cache.is_empty());
16663    }
16664
16665    #[test]
16666    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
16667        let cyclic = epsilon_cycle_atn();
16668        let acyclic = token_then_eof_atn();
16669        let mut parser = mini_parser(Vec::new());
16670
16671        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
16672        assert_eq!(
16673            parser.empty_cycle_cache_atn,
16674            Some(SharedAtnCacheKey::for_atn(&cyclic))
16675        );
16676        assert_eq!(parser.empty_cycle_cache[1], Some(true));
16677
16678        parser.reset_per_parse_caches();
16679        assert_eq!(parser.empty_cycle_cache[1], Some(true));
16680        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
16681
16682        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
16683        assert_eq!(
16684            parser.empty_cycle_cache_atn,
16685            Some(SharedAtnCacheKey::for_atn(&acyclic))
16686        );
16687        assert_eq!(parser.empty_cycle_cache[1], Some(false));
16688    }
16689
16690    #[test]
16691    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
16692        let source = Source {
16693            tokens: vec![
16694                TestToken::new(1).with_text("x"),
16695                TestToken::eof("parser-test", 1, 1, 1),
16696            ],
16697            index: 0,
16698        };
16699        let data = RecognizerData::new(
16700            "Mini.g4",
16701            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16702        );
16703        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
16704        let expected = ExpectedTokens {
16705            index: Some(0),
16706            symbols: BTreeSet::new(),
16707            no_viable: None,
16708        };
16709
16710        let (_, message) = parser.expected_error_message(0, 0, &expected);
16711
16712        assert_eq!(message, "mismatched input 'x'");
16713    }
16714
16715    #[test]
16716    fn eof_rule_stop_index_points_at_eof_token() {
16717        let source = Source {
16718            tokens: vec![
16719                TestToken::new(1).with_text("x"),
16720                TestToken::eof("parser-test", 1, 1, 1),
16721            ],
16722            index: 0,
16723        };
16724        let data = RecognizerData::new(
16725            "Mini.g4",
16726            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16727        );
16728        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
16729
16730        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
16731        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
16732    }
16733
16734    #[test]
16735    fn generated_parser_action_uses_current_rule_stop_boundary() {
16736        let mut parser = mini_parser(vec![
16737            TestToken::new(1).with_text("x"),
16738            TestToken::eof("parser-test", 1, 1, 1),
16739        ]);
16740
16741        parser.match_token(1).expect("token should match");
16742        let action = parser.parser_action_at_current(7, 0, 0, false);
16743        assert_eq!(action.source_state(), 7);
16744        assert_eq!(action.rule_index(), 0);
16745        assert_eq!(action.start_index(), 0);
16746        assert_eq!(action.stop_index(), Some(0));
16747
16748        parser.match_eof().expect("EOF should match");
16749        let action = parser.parser_action_at_current(8, 0, 0, true);
16750        assert_eq!(action.stop_index(), Some(1));
16751    }
16752
16753    #[test]
16754    fn folds_left_recursive_boundary_into_rule_node() {
16755        let mut arena = RecognitionArena::default();
16756        let first = arena.push_node(ArenaRecognizedNode::Token {
16757            token: TokenId::try_from(0).expect("test token ID"),
16758        });
16759        let boundary =
16760            arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: 1 });
16761        let second = arena.push_node(ArenaRecognizedNode::Token {
16762            token: TokenId::try_from(1).expect("test token ID"),
16763        });
16764        let mut nodes = NodeSeqId::EMPTY;
16765        for node in [first, boundary, second].into_iter().rev() {
16766            nodes = arena.prepend(nodes, node);
16767        }
16768
16769        let folded = arena.fold_left_recursive_boundaries(nodes);
16770        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
16771
16772        assert_eq!(folded_nodes.len(), 2);
16773        let ArenaRecognizedNode::Rule {
16774            rule_index,
16775            invoking_state,
16776            start_index,
16777            stop_index,
16778            children,
16779            ..
16780        } = arena.node(folded_nodes[0])
16781        else {
16782            panic!("first folded node should be a rule");
16783        };
16784        assert_eq!(rule_index, 1);
16785        assert_eq!(invoking_state, -1);
16786        assert_eq!(start_index, 0);
16787        assert_eq!(stop_index, Some(0));
16788        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
16789        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
16790
16791        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
16792        assert_eq!(
16793            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16794            (4, 3, 1)
16795        );
16796        assert_eq!(
16797            (stats.total_links, stats.live_links, stats.dead_links),
16798            (9, 3, 6)
16799        );
16800    }
16801
16802    #[test]
16803    fn recognition_arena_reports_live_dead_and_retained_capacity() {
16804        let mut arena = RecognitionArena::default();
16805        let token = arena.push_node(ArenaRecognizedNode::Token {
16806            token: TokenId::try_from(0).expect("test token ID"),
16807        });
16808        let extra = arena.push_extra(RecognitionExtra::MissingToken {
16809            token_type: 2,
16810            at_index: 1,
16811            text: "<missing X>".to_owned(),
16812        });
16813        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
16814        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
16815            token: TokenId::try_from(1).expect("test token ID"),
16816        });
16817        let mut live = NodeSeqId::EMPTY;
16818        live = arena.prepend(live, missing);
16819        live = arena.prepend(live, token);
16820        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
16821        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
16822            line: 1,
16823            column: 0,
16824            message: "missing X".to_owned(),
16825        }]);
16826        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
16827            line: 1,
16828            column: 1,
16829            message: "discarded".to_owned(),
16830        }]);
16831        let deferred_children = arena.deferred_fragment(live);
16832        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
16833            rule_index: 0,
16834            invoking_state: -1,
16835            start_index: 0,
16836            stop_index: Some(1),
16837            deferred_children,
16838            children: NodeSeqId::EMPTY,
16839        });
16840
16841        let stats = arena.stats(live, live_diagnostics);
16842
16843        assert_eq!(
16844            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16845            (3, 2, 1)
16846        );
16847        assert_eq!(
16848            (stats.total_links, stats.live_links, stats.dead_links),
16849            (5, 3, 2)
16850        );
16851        assert_eq!(
16852            (stats.total_extras, stats.live_extras, stats.dead_extras),
16853            (3, 2, 1)
16854        );
16855        assert!(size_of::<SeqLink>() <= 8);
16856        assert!(size_of::<DiagnosticLink>() <= 8);
16857        assert!(size_of::<FastDeferredNode>() <= 12);
16858        assert!(size_of::<FastDeferredRule>() <= 28);
16859        assert!(size_of::<FastRecognizeOutcome>() <= 24);
16860        let capacities = (
16861            stats.node_capacity,
16862            stats.link_capacity,
16863            stats.extra_capacity,
16864        );
16865        let deferred_capacities = (
16866            arena.deferred_nodes.capacity(),
16867            arena.deferred_rules.capacity(),
16868        );
16869
16870        arena.reset();
16871        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
16872        assert_eq!(
16873            (reset.total_nodes, reset.total_links, reset.total_extras),
16874            (0, 0, 0)
16875        );
16876        assert_eq!(
16877            (
16878                reset.node_capacity,
16879                reset.link_capacity,
16880                reset.extra_capacity,
16881            ),
16882            capacities
16883        );
16884        assert!(arena.deferred_nodes.is_empty());
16885        assert!(arena.deferred_rules.is_empty());
16886        assert_eq!(
16887            (
16888                arena.deferred_nodes.capacity(),
16889                arena.deferred_rules.capacity(),
16890            ),
16891            deferred_capacities
16892        );
16893    }
16894
16895    #[test]
16896    fn parser_computes_recognition_arena_stats_on_demand() {
16897        let mut parser = mini_parser(Vec::new());
16898        let live = parser
16899            .recognition_arena
16900            .push_node(ArenaRecognizedNode::Token {
16901                token: TokenId::try_from(0).expect("test token ID"),
16902            });
16903        let discarded = parser
16904            .recognition_arena
16905            .push_node(ArenaRecognizedNode::ErrorToken {
16906                token: TokenId::try_from(1).expect("test token ID"),
16907            });
16908        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
16909        let _discarded_root = parser
16910            .recognition_arena
16911            .prepend(NodeSeqId::EMPTY, discarded);
16912        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
16913
16914        let stats = parser.recognition_arena_stats();
16915
16916        assert_eq!(
16917            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16918            (2, 1, 1)
16919        );
16920        assert_eq!(
16921            (stats.total_links, stats.live_links, stats.dead_links),
16922            (2, 1, 1)
16923        );
16924    }
16925
16926    #[test]
16927    fn recognition_arena_drops_capacity_above_retention_limit() {
16928        let mut storage = Vec::<u8>::with_capacity(4);
16929        storage.extend([1, 2, 3]);
16930
16931        reset_arena_vec(&mut storage, 3);
16932
16933        assert!(storage.is_empty());
16934        assert_eq!(storage.capacity(), 0);
16935    }
16936
16937    #[test]
16938    fn recognition_arena_concatenates_diagnostics_in_source_order() {
16939        let mut arena = RecognitionArena::default();
16940        let prefix = arena.diagnostic_sequence([
16941            ParserDiagnostic {
16942                line: 1,
16943                column: 0,
16944                message: "first".to_owned(),
16945            },
16946            ParserDiagnostic {
16947                line: 1,
16948                column: 1,
16949                message: "second".to_owned(),
16950            },
16951        ]);
16952        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
16953            line: 1,
16954            column: 2,
16955            message: "third".to_owned(),
16956        }]);
16957        let extras_before = arena.extras.len();
16958
16959        let combined = arena.concat_diagnostics(prefix, suffix);
16960        let messages = arena
16961            .diagnostics(combined)
16962            .map(|diagnostic| diagnostic.message.as_str())
16963            .collect::<Vec<_>>();
16964
16965        assert_eq!(messages, ["first", "second", "third"]);
16966        assert_eq!(arena.extras.len(), extras_before);
16967    }
16968
16969    #[test]
16970    fn outcome_ties_keep_later_non_recursive_alternative() {
16971        let arena = RecognitionArena::default();
16972        let first = RecognizeOutcome {
16973            index: 1,
16974            consumed_eof: false,
16975            alt_number: 0,
16976            member_values: BTreeMap::new(),
16977            return_values: BTreeMap::new(),
16978            diagnostics: DiagnosticSeqId::EMPTY,
16979            decisions: Vec::new(),
16980            actions: vec![ParserAction::new(1, 0, 0, None)],
16981            nodes: NodeSeqId::EMPTY,
16982        };
16983        let second = RecognizeOutcome {
16984            actions: vec![ParserAction::new(2, 0, 0, None)],
16985            ..first.clone()
16986        };
16987
16988        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
16989            .expect("one outcome should be selected");
16990        assert_eq!(selected.actions[0].source_state(), 2);
16991    }
16992
16993    #[test]
16994    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
16995        let arena = RecognitionArena::default();
16996        let first = RecognizeOutcome {
16997            index: 1,
16998            consumed_eof: false,
16999            alt_number: 0,
17000            member_values: BTreeMap::new(),
17001            return_values: BTreeMap::new(),
17002            diagnostics: DiagnosticSeqId::EMPTY,
17003            decisions: Vec::new(),
17004            actions: vec![ParserAction::new(1, 0, 0, None)],
17005            nodes: NodeSeqId::EMPTY,
17006        };
17007        let second = RecognizeOutcome {
17008            actions: vec![
17009                ParserAction::new(2, 0, 0, None),
17010                ParserAction::new(3, 0, 0, None),
17011            ],
17012            ..first.clone()
17013        };
17014
17015        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
17016            .expect("one outcome should be selected");
17017        assert_eq!(selected.actions.len(), 2);
17018    }
17019
17020    #[test]
17021    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
17022        let arena = RecognitionArena::default();
17023        let first = RecognizeOutcome {
17024            index: 7,
17025            consumed_eof: false,
17026            alt_number: 0,
17027            member_values: BTreeMap::new(),
17028            return_values: BTreeMap::new(),
17029            diagnostics: DiagnosticSeqId::EMPTY,
17030            decisions: vec![1, 0],
17031            actions: vec![
17032                ParserAction::new(23, 2, 2, Some(4)),
17033                ParserAction::new(23, 2, 0, Some(6)),
17034            ],
17035            nodes: NodeSeqId::EMPTY,
17036        };
17037        let second = RecognizeOutcome {
17038            decisions: vec![0, 1],
17039            actions: vec![
17040                ParserAction::new(23, 2, 2, Some(6)),
17041                ParserAction::new(23, 2, 0, Some(6)),
17042            ],
17043            ..first.clone()
17044        };
17045
17046        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17047            .expect("one outcome should be selected");
17048        assert_eq!(selected.actions[0].stop_index(), Some(6));
17049    }
17050
17051    #[test]
17052    fn outcome_ties_keep_first_recursive_tree_shape() {
17053        let mut arena = RecognitionArena::default();
17054        let token = arena.push_node(ArenaRecognizedNode::Token {
17055            token: TokenId::try_from(0).expect("test token ID"),
17056        });
17057        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
17058        let inner = arena.push_node(ArenaRecognizedNode::Rule {
17059            rule_index: 1,
17060            invoking_state: -1,
17061            alt_number: 0,
17062            start_index: 0,
17063            stop_index: Some(0),
17064            return_values: None,
17065            children: token_children,
17066        });
17067        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
17068        let outer = arena.push_node(ArenaRecognizedNode::Rule {
17069            rule_index: 1,
17070            invoking_state: -1,
17071            alt_number: 0,
17072            start_index: 0,
17073            stop_index: Some(0),
17074            return_values: None,
17075            children: inner_children,
17076        });
17077        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
17078        let first = RecognizeOutcome {
17079            index: 1,
17080            consumed_eof: false,
17081            alt_number: 0,
17082            member_values: BTreeMap::new(),
17083            return_values: BTreeMap::new(),
17084            diagnostics: DiagnosticSeqId::EMPTY,
17085            decisions: Vec::new(),
17086            actions: vec![ParserAction::new(1, 0, 0, None)],
17087            nodes: recursive_nodes,
17088        };
17089        let second = RecognizeOutcome {
17090            index: 1,
17091            consumed_eof: false,
17092            alt_number: 0,
17093            member_values: BTreeMap::new(),
17094            return_values: BTreeMap::new(),
17095            diagnostics: DiagnosticSeqId::EMPTY,
17096            decisions: Vec::new(),
17097            actions: vec![ParserAction::new(2, 0, 0, None)],
17098            nodes: recursive_nodes,
17099        };
17100
17101        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17102            .expect("one outcome should be selected");
17103        assert_eq!(selected.actions[0].source_state(), 1);
17104    }
17105
17106    #[test]
17107    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
17108        let mut arena = RecognitionArena::default();
17109        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17110            line: 1,
17111            column: 3,
17112            message: "missing 'Y' at '<EOF>'".to_owned(),
17113        }]);
17114        let first_alt = RecognizeOutcome {
17115            index: 2,
17116            consumed_eof: true,
17117            alt_number: 0,
17118            member_values: BTreeMap::new(),
17119            return_values: BTreeMap::new(),
17120            diagnostics: recovered_diagnostics,
17121            decisions: vec![0],
17122            actions: vec![ParserAction::new(1, 0, 0, None)],
17123            nodes: NodeSeqId::EMPTY,
17124        };
17125        let second_alt = RecognizeOutcome {
17126            diagnostics: DiagnosticSeqId::EMPTY,
17127            decisions: vec![1],
17128            actions: vec![ParserAction::new(2, 0, 0, None)],
17129            ..first_alt.clone()
17130        };
17131
17132        let selected = select_best_outcome(
17133            [second_alt, first_alt].into_iter(),
17134            PredictionMode::Sll,
17135            &arena,
17136        )
17137        .expect("one outcome should be selected");
17138        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
17139        assert_eq!(selected.decisions, [0]);
17140    }
17141}