Skip to main content

antlr4_runtime/
parser.rs

1// `HashMap`/`HashSet` here are used as parser-internal caches keyed on
2// stable ATN coordinates (state numbers, token indices). They're never
3// iterated externally, so the project's `disallowed_types` lint (which
4// guards against non-deterministic iteration order leaking out) does not
5// apply to these uses.
6use std::cell::RefCell;
7use std::cmp::Ordering;
8#[allow(clippy::disallowed_types)]
9use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
10use std::hash::{BuildHasherDefault, Hash, Hasher};
11use std::rc::Rc;
12
13/// Rotate constant copied from rustc-hash / `FxHash`. The default
14/// `RandomState` hasher seeds itself from the OS RNG and runs `SipHash` on
15/// every key, which dominates `recognize_state_fast`'s memo lookups;
16/// `FxHasher` is a streaming integer hasher with near-zero per-call overhead
17/// and matches the access pattern of small integer keys that the parser memo
18/// uses.
19#[derive(Clone, Copy, Default)]
20struct FxHasher {
21    hash: u64,
22}
23
24const FX_ROT: u32 = 5;
25const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
26
27impl Hasher for FxHasher {
28    /// Folds bytes 8 at a time so a `write(&[u8; 8])` call hashes to the same
29    /// state as a `write_u64` of the same little-endian bits. The `Hash` impls
30    /// for `String`, `[u8; N]`, and slice-like types reach the hasher through
31    /// `write`; matching the typed-method behaviour avoids the silent
32    /// divergence flagged in PR #5 review (Greptile P2). Tail bytes that do
33    /// not form a full word are mixed one at a time with the same constants,
34    /// keeping behaviour deterministic regardless of the slice length.
35    #[inline]
36    fn write(&mut self, mut bytes: &[u8]) {
37        while bytes.len() >= 8 {
38            let (head, rest) = bytes.split_at(8);
39            let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
40            self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
41            bytes = rest;
42        }
43        for byte in bytes {
44            self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(*byte)).wrapping_mul(FX_SEED);
45        }
46    }
47    #[inline]
48    fn write_u64(&mut self, value: u64) {
49        self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
50    }
51    #[inline]
52    fn write_usize(&mut self, value: usize) {
53        self.write_u64(value as u64);
54    }
55    #[inline]
56    fn write_u32(&mut self, value: u32) {
57        self.write_u64(u64::from(value));
58    }
59    #[inline]
60    fn write_i32(&mut self, value: i32) {
61        self.write_u64(u64::from(i32::cast_unsigned(value)));
62    }
63    #[inline]
64    fn finish(&self) -> u64 {
65        self.hash
66    }
67}
68
69type FxBuildHasher = BuildHasherDefault<FxHasher>;
70#[allow(clippy::disallowed_types)]
71type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
72#[allow(clippy::disallowed_types)]
73type FxHashSet<K> = HashSet<K, FxBuildHasher>;
74
75use crate::atn::AtnStateKind;
76use crate::atn::parser::{
77    ParserAtnPrediction, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
78};
79use crate::atn::parser_atn::{
80    ParserAtn as Atn, ParserAtnState as AtnState, ParserIntervalSet, ParserTransition,
81    ParserTransitionData as Transition, ParserTransitionKind,
82};
83#[cfg(test)]
84use crate::atn::parser_atn::{ParserAtnBuilder, ParserTransitionSpec};
85use crate::char_stream::CharStream;
86use crate::errors::AntlrError;
87use crate::int_stream::IntStream;
88use crate::lexer::{LexerCustomAction, LexerLifecycleCtx, LexerSemCtx};
89use crate::recognizer::{Recognizer, RecognizerData};
90use crate::semir::{self, AStmt, ArithOp, CmpOp, ExprId, HookId, PExpr, SemIr, StmtId};
91use crate::token::{
92    TOKEN_EOF, Token, TokenId, TokenSource, TokenSourceError, TokenSpec, TokenStore, TokenView,
93};
94use crate::token_stream::CommonTokenStream;
95use crate::tree::{
96    Node, NodeId, ParseTreeCheckpoint, ParseTreeStorage, ParsedFile, ParserRuleContext,
97};
98use crate::vocabulary::Vocabulary;
99
100type ParseTree = NodeId;
101
102/// Upper bound for the recursive metadata recognizer before it treats a path as
103/// non-viable. Long expression-regression descriptors legitimately walk tens
104/// of thousands of ATN edges.
105const RECOGNITION_DEPTH_LIMIT: usize = 32_768;
106/// Preserve the recursive hot path while checking native stack capacity often
107/// enough that one unchecked group cannot cross the protected red zone.
108const FAST_RECOGNIZE_STACK_CHECK_INTERVAL: usize = 8;
109const FAST_RECOGNIZE_RED_ZONE: usize = 1024 * 1024;
110const FAST_RECOGNIZE_STACK_SIZE: usize = 4 * 1024 * 1024;
111/// Whole-rule direct adaptive execution is allowed to give up and fall back to
112/// the existing recognizer. Keep the guard at the same order of magnitude as
113/// speculative recognition so malformed cyclic ATNs cannot spin forever.
114const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
115/// Probe window for deciding whether clean-pass memo entries are reusable
116/// enough to keep caching. High-cardinality parses mostly produce one-shot
117/// entries; compact ambiguous loops repeatedly hit the same keys.
118const CLEAN_MEMO_PROBE_LIMIT: usize = 4096;
119const CLEAN_MEMO_REPEAT_LIMIT: usize = 8;
120/// Sparse parses periodically reopen the bounded probe so a repeat-heavy
121/// region that starts later in the token stream can promote memoization.
122const CLEAN_MEMO_REPROBE_INTERVAL: usize = 262_144;
123const FAST_RECOGNIZE_VISITING_CAPACITY: usize = 256;
124const FAST_RECOGNIZE_MIN_MEMO_CAPACITY: usize = 256;
125const FAST_RECOGNIZE_MAX_MEMO_CAPACITY: usize = 524_288;
126const FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY: usize = 65_536;
127
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129enum CleanMemoMode {
130    Probe,
131    Promote,
132    Sparse,
133}
134
135fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
136    intervals
137        .iter()
138        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
139}
140
141fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
142    let mut symbols = BTreeSet::new();
143    for (start, stop) in intervals {
144        symbols.extend(*start..=*stop);
145    }
146    symbols
147}
148
149fn interval_complement_symbols(
150    intervals: &[(i32, i32)],
151    min_vocabulary: i32,
152    max_vocabulary: i32,
153) -> BTreeSet<i32> {
154    (min_vocabulary..=max_vocabulary)
155        .filter(|symbol| !interval_set_contains(intervals, *symbol))
156        .collect()
157}
158
159#[cfg(feature = "perf-counters")]
160mod perf_counters {
161    use std::cell::Cell;
162    thread_local! {
163        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
164        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
165        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
166        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
167        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
168        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
169        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
170        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
171        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
172        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
173        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
174        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
175        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
176    }
177    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
178        c.with(|v| v.set(v.get() + n));
179    }
180    thread_local! {
181        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
182        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
183        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
184        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
185        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
186        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
187        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
188        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
189        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
190        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
191        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
192    }
193    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
194        [
195            ("rfs_calls", RFS_CALLS.with(Cell::get)),
196            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
197            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
198            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
199            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
200            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
201            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
202            (
203                "outcome_dedupe_inputs",
204                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
205            ),
206            (
207                "outcome_dedupe_removed",
208                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
209            ),
210            (
211                "outcome_dedupe_inline",
212                OUTCOME_DEDUPE_INLINE.with(Cell::get),
213            ),
214            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
215            (
216                "outcome_dedupe_sparse",
217                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
218            ),
219            (
220                "outcome_dedupe_dense_words",
221                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
222            ),
223            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
224            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
225            (
226                "atom_range_transitions",
227                ATOM_RANGE_TRANSITIONS.with(Cell::get),
228            ),
229            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
230            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
231            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
232            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
233            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
234            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
235            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
236            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
237        ]
238    }
239    pub fn reset() {
240        RFS_CALLS.with(|c| c.set(0));
241        RFS_MEMO_HITS.with(|c| c.set(0));
242        RFS_MEMO_MISSES.with(|c| c.set(0));
243        RFS_VISITING_CYCLE.with(|c| c.set(0));
244        MEMO_INSERTED.with(|c| c.set(0));
245        OUTCOMES_PUSHED.with(|c| c.set(0));
246        OUTCOMES_CLONED.with(|c| c.set(0));
247        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
248        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
249        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
250        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
251        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
252        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
253        EPSILON_TRANSITIONS.with(|c| c.set(0));
254        RULE_TRANSITIONS.with(|c| c.set(0));
255        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
256        SINGLE_TRANS_BODY.with(|c| c.set(0));
257        MULTI_TRANS_BODY.with(|c| c.set(0));
258        SINGLE_TRANS_RULE.with(|c| c.set(0));
259        SINGLE_TRANS_ATOM.with(|c| c.set(0));
260        SINGLE_TRANS_OTHER.with(|c| c.set(0));
261        OUTCOMES_RETURN_0.with(|c| c.set(0));
262        OUTCOMES_RETURN_1.with(|c| c.set(0));
263        OUTCOMES_RETURN_N.with(|c| c.set(0));
264    }
265    pub fn dump() {
266        for (name, value) in snapshot() {
267            #[allow(clippy::print_stderr)]
268            {
269                eprintln!("perf {name}={value}");
270            }
271        }
272    }
273}
274
275#[cfg(feature = "perf-counters")]
276pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
277/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
278/// fast recognizer has probed far enough that per-token stream sync dominates.
279/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
280/// switching long inputs to the cheaper filled-stream path before large fanout.
281const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
282/// Parser semantic action reached while recognizing one ATN path.
283///
284/// Generated parsers use `source_state` to dispatch back to the grammar action
285/// rendered for that ATN action transition. The token interval is the current
286/// rule's input span at the action site, which covers common target templates
287/// such as `$text`. Rule-init actions do not have an ATN action source state,
288/// so they are marked separately and may carry an ATN state for expected-token
289/// rendering.
290#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
291pub struct ParserAction {
292    source_state: usize,
293    rule_index: usize,
294    start_index: usize,
295    stop_index: Option<usize>,
296    rule_init: bool,
297    expected_state: Option<usize>,
298}
299
300impl ParserAction {
301    /// Creates an action event for a recognized parser path.
302    pub const fn new(
303        source_state: usize,
304        rule_index: usize,
305        start_index: usize,
306        stop_index: Option<usize>,
307    ) -> Self {
308        Self {
309            source_state,
310            rule_index,
311            start_index,
312            stop_index,
313            rule_init: false,
314            expected_state: None,
315        }
316    }
317
318    /// Creates an action event for a rule-level `@init` action.
319    pub const fn new_rule_init(
320        rule_index: usize,
321        start_index: usize,
322        expected_state: Option<usize>,
323    ) -> Self {
324        Self {
325            source_state: usize::MAX,
326            rule_index,
327            start_index,
328            stop_index: None,
329            rule_init: true,
330            expected_state,
331        }
332    }
333
334    /// ATN state that owns the semantic-action transition.
335    pub const fn source_state(&self) -> usize {
336        self.source_state
337    }
338
339    /// Grammar rule index recorded by the serialized ATN action transition.
340    pub const fn rule_index(&self) -> usize {
341        self.rule_index
342    }
343
344    /// Token-stream index where the active rule began.
345    pub const fn start_index(&self) -> usize {
346        self.start_index
347    }
348
349    /// Last token-stream index consumed before the action was reached.
350    pub const fn stop_index(&self) -> Option<usize> {
351        self.stop_index
352    }
353
354    /// Reports whether this event represents a rule-level `@init` action.
355    pub const fn is_rule_init(&self) -> bool {
356        self.rule_init
357    }
358
359    /// ATN state used to compute expected-token display for this action.
360    pub const fn expected_state(&self) -> Option<usize> {
361        self.expected_state
362    }
363}
364
365/// Runtime view passed to parser semantic hooks.
366///
367/// The context is intentionally read-only with respect to parser structure:
368/// predicates may run speculatively during prediction, and hooks can be called
369/// more than once for paths that are later abandoned. Lookahead methods may
370/// buffer tokens from the underlying token source, matching normal parser
371/// prediction behavior.
372pub struct ParserSemCtx<'a, S>
373where
374    S: TokenSource,
375{
376    input: &'a mut CommonTokenStream<S>,
377    tree_storage: &'a ParseTreeStorage,
378    rule_index: usize,
379    coordinate_index: usize,
380    rule_name: Option<String>,
381    context: Option<&'a ParserRuleContext>,
382    tree: Option<ParseTree>,
383    local_int_arg: Option<(usize, i64)>,
384    member_values: &'a BTreeMap<usize, i64>,
385    action: Option<ParserAction>,
386}
387
388impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
389where
390    S: TokenSource,
391{
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        f.debug_struct("ParserSemCtx")
394            .field("rule_index", &self.rule_index)
395            .field("coordinate_index", &self.coordinate_index)
396            .field("rule_name", &self.rule_name)
397            .field("context", &self.context)
398            .field("tree", &self.tree)
399            .field("local_int_arg", &self.local_int_arg)
400            .field("member_values", &self.member_values)
401            .field("action", &self.action)
402            .finish_non_exhaustive()
403    }
404}
405
406impl<'a, S> ParserSemCtx<'a, S>
407where
408    S: TokenSource,
409{
410    /// Rule index that owns the predicate/action coordinate.
411    #[must_use]
412    pub const fn rule_index(&self) -> usize {
413        self.rule_index
414    }
415
416    /// Rule name that owns the coordinate, when recognizer metadata has it.
417    #[must_use]
418    pub fn rule_name(&self) -> Option<&str> {
419        self.rule_name.as_deref()
420    }
421
422    /// Predicate/action index inside the owning rule. Parser actions keyed only
423    /// by ATN source state report `usize::MAX` here; use [`Self::action`] for
424    /// the stable action event.
425    #[must_use]
426    pub const fn coordinate_index(&self) -> usize {
427        self.coordinate_index
428    }
429
430    /// Current token-stream index.
431    #[must_use]
432    pub fn input_index(&self) -> usize {
433        self.input.index()
434    }
435
436    /// Token type at one-based lookahead/lookbehind offset.
437    pub fn la(&mut self, offset: isize) -> i32 {
438        self.input.la(offset)
439    }
440
441    /// Token at one-based lookahead/lookbehind offset.
442    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
443        self.input.lt(offset)
444    }
445
446    /// Borrowing token view for text inspection at a one-based offset.
447    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
448        self.lt(offset)
449    }
450
451    /// Token at an absolute buffered index, including hidden/custom channels.
452    ///
453    /// Unlike [`Self::lt`], this does not apply the token stream's channel
454    /// filter and does not move its cursor. It is intended for semantic helpers
455    /// such as automatic-semicolon-insertion checks that inspect trivia
456    /// immediately before the current visible token.
457    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
458        self.input.get(index)
459    }
460
461    /// Current generated rule context, when a generated rule predicate supplied
462    /// one.
463    #[must_use]
464    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
465        self.context
466    }
467
468    /// Flat tree storage containing completed children visible to this hook.
469    #[must_use]
470    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
471        self.tree_storage
472    }
473
474    /// Canonical token store used by completed flat-tree nodes.
475    #[must_use]
476    pub const fn token_store(&self) -> &TokenStore {
477        self.input.token_store()
478    }
479
480    /// Completed parse-tree root ID passed to a replayed action hook.
481    #[must_use]
482    pub const fn tree_id(&self) -> Option<NodeId> {
483        self.tree
484    }
485
486    /// Completed parse tree passed to an action hook, if the action is being
487    /// replayed after recognition.
488    #[must_use]
489    pub fn tree(&self) -> Option<Node<'_>> {
490        self.tree
491            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
492    }
493
494    /// Integer local argument visible to this predicate coordinate.
495    #[must_use]
496    pub fn local_int_arg(&self) -> Option<i64> {
497        self.local_int_arg.map(|(_, value)| value)
498    }
499
500    /// Integer member value observed on the current speculative path.
501    #[must_use]
502    pub fn member_int(&self, member: usize) -> Option<i64> {
503        self.member_values.get(&member).copied()
504    }
505
506    /// Parser action event being replayed, when this context belongs to an
507    /// action hook.
508    #[must_use]
509    pub const fn action(&self) -> Option<ParserAction> {
510        self.action
511    }
512
513    /// Text covered by a parser action event.
514    ///
515    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
516    /// EOF the interval ends at the previous *visible* token, so trailing hidden
517    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
518    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
519    /// itself guards `start > stop`, so an empty interval yields `""`.
520    pub fn action_text(&self) -> String {
521        let Some(action) = self.action else {
522            return String::new();
523        };
524        let Some(stop) = action.stop_index() else {
525            return String::new();
526        };
527        let stop = if self
528            .input
529            .get(stop)
530            .is_some_and(|token| token.token_type() == TOKEN_EOF)
531        {
532            let Some(previous) = self.input.previous_visible_token_index(stop) else {
533                return String::new();
534            };
535            previous
536        } else {
537            stop
538        };
539        self.input.text(action.start_index(), stop)
540    }
541}
542
543/// User extension point for parser semantic predicates and actions that the
544/// metadata generator did not translate into built-in runtime metadata.
545///
546/// Returning `None`/`false` says "not handled", so the runtime falls through
547/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
548/// speculative prediction and must be replay-safe.
549pub trait SemanticHooks {
550    /// Whether generated lexers should route lifecycle callbacks through this
551    /// hook object.
552    ///
553    /// User hook implementations opt in by default. [`NoSemanticHooks`]
554    /// overrides this to keep generated lexers on the direct no-extension
555    /// token path.
556    const ENABLES_LEXER_LIFECYCLE: bool = true;
557
558    /// Whether this hook object may observe parser predicate transitions.
559    ///
560    /// Custom hooks default to conservative predicate handling so the fast
561    /// recognizer does not bypass a `sempred` implementation.
562    fn observes_parser_predicates(&self) -> bool {
563        true
564    }
565
566    fn sempred<S>(
567        &mut self,
568        ctx: &mut ParserSemCtx<'_, S>,
569        rule_index: usize,
570        pred_index: usize,
571    ) -> Option<bool>
572    where
573        S: TokenSource,
574    {
575        let _ = (ctx, rule_index, pred_index);
576        None
577    }
578
579    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
580    where
581        S: TokenSource,
582    {
583        let _ = (ctx, action);
584        false
585    }
586
587    fn lexer_sempred<I>(
588        &mut self,
589        ctx: &mut LexerSemCtx<'_, I>,
590        rule_index: usize,
591        pred_index: usize,
592    ) -> Option<bool>
593    where
594        I: CharStream,
595    {
596        let _ = (ctx, rule_index, pred_index);
597        None
598    }
599
600    /// Runs a lexer custom action on the committed lexing path. Returns whether
601    /// the hook handled the action.
602    ///
603    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
604    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
605    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
606    /// queued prefix tokens, just like the closure-based `custom_action` API.
607    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
608    /// borrow, so those mutators are inert there.)
609    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
610    where
611        I: CharStream,
612    {
613        let _ = (ctx, action);
614        false
615    }
616
617    /// Runs after runtime-owned lexer state has been reset for reuse.
618    ///
619    /// Implementations should clear extension-owned transient state here.
620    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
621    where
622        I: CharStream,
623    {
624        let _ = ctx;
625    }
626
627    /// Runs before the runtime returns a queued token or starts a new ATN
628    /// token match.
629    ///
630    /// The callback also runs between internal `skip`/`more` matches, so it
631    /// observes every point where another ATN match may start.
632    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
633    where
634        I: CharStream,
635    {
636        let _ = ctx;
637    }
638
639    /// Runs after the accepted path's portable and custom actions, but before
640    /// the token span is finalized and emitted.
641    ///
642    /// Accepted paths that selected `skip` or `more` are included, and the hook
643    /// may observe or override that pending token type.
644    ///
645    /// This callback has no synthetic ATN coordinate. It therefore also runs
646    /// for accepted rules that contain no action or predicate.
647    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
648    where
649        I: CharStream,
650    {
651        let _ = ctx;
652    }
653
654    /// Observes a token after committed lexer actions and portable commands
655    /// have run and the token has been emitted, immediately before it is
656    /// returned to the token stream.
657    ///
658    /// Hidden and custom-channel tokens are included. `skip` and intermediate
659    /// `more` matches do not produce callbacks.
660    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
661        let _ = token;
662    }
663}
664
665/// Default hook object used by parsers that do not need user-supplied
666/// semantics.
667#[derive(Clone, Copy, Debug, Default)]
668pub struct NoSemanticHooks;
669
670impl SemanticHooks for NoSemanticHooks {
671    const ENABLES_LEXER_LIFECYCLE: bool = false;
672
673    fn observes_parser_predicates(&self) -> bool {
674        false
675    }
676}
677
678/// Parser semantic predicate rendered from a supported target template.
679///
680/// The metadata recognizer evaluates these at the token-stream index where the
681/// predicate transition is reached. Unsupported or absent predicate templates
682/// remain unconditional so existing generated parsers keep their previous
683/// behavior unless the generator opts into this table.
684#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
685pub enum ParserPredicate {
686    True,
687    False,
688    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
689    FalseWithMessage {
690        message: &'static str,
691    },
692    /// Target-template test helper that reports predicate evaluation before
693    /// returning the wrapped boolean value.
694    Invoke {
695        value: bool,
696    },
697    LookaheadTextEquals {
698        offset: isize,
699        text: &'static str,
700    },
701    LookaheadNotEquals {
702        offset: isize,
703        token_type: i32,
704    },
705    /// Checks that the last two consumed visible tokens were adjacent in the
706    /// token stream. Used by C# parser predicates for split operator tokens.
707    TokenPairAdjacent,
708    /// Checks a generated parser context child by rule index and text.
709    ///
710    /// If the child is absent the predicate succeeds, matching target helpers
711    /// that treat incomplete or non-matching contexts as non-restrictive.
712    ContextChildRuleTextNotEquals {
713        rule_index: usize,
714        text: &'static str,
715    },
716    /// Compares the current rule invocation's integer argument with a literal
717    /// value from a supported `ValEquals("$i", "...")` target template.
718    LocalIntEquals {
719        value: i64,
720    },
721    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
722    /// rule invocation's integer argument.
723    LocalIntLessOrEqual {
724        value: i64,
725    },
726    /// Compares a generated parser integer member modulo a literal value.
727    MemberModuloEquals {
728        member: usize,
729        modulus: i64,
730        value: i64,
731        equals: bool,
732    },
733    /// Compares a generated parser integer member with a literal value.
734    MemberEquals {
735        member: usize,
736        value: i64,
737        equals: bool,
738    },
739}
740
741impl ParserPredicate {
742    /// Lowers the legacy predicate metadata variant into `SemIR`.
743    ///
744    /// This is the compatibility adapter for generated parsers produced while
745    /// the runtime still emitted closed enum tables. Newer generated parsers
746    /// emit `SemIR` directly.
747    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
748        match self {
749            Self::True => ir.expr(PExpr::Bool(true)),
750            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
751            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
752            Self::LookaheadTextEquals { offset, text } => {
753                let token = ir.expr(PExpr::TokenText(offset));
754                let text = ir.intern(text);
755                let text = ir.expr(PExpr::Str(text));
756                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
757            }
758            Self::LookaheadNotEquals { offset, token_type } => {
759                let actual = ir.expr(PExpr::La(offset));
760                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
761                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
762            }
763            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
764            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
765                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
766                let expected = ir.intern(text);
767                let expected = ir.expr(PExpr::Str(expected));
768                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
769            }
770            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
771            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
772            Self::MemberModuloEquals {
773                member,
774                modulus,
775                value,
776                equals,
777            } => {
778                if modulus == 0 {
779                    return ir.expr(PExpr::Bool(false));
780                }
781                let member = ir.expr(PExpr::Member(member));
782                let modulus = ir.expr(PExpr::Int(modulus));
783                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
784                let expected = ir.expr(PExpr::Int(value));
785                ir.expr(PExpr::Cmp(
786                    if equals { CmpOp::Eq } else { CmpOp::Ne },
787                    actual,
788                    expected,
789                ))
790            }
791            Self::MemberEquals {
792                member,
793                value,
794                equals,
795            } => {
796                let actual = ir.expr(PExpr::Member(member));
797                let expected = ir.expr(PExpr::Int(value));
798                ir.expr(PExpr::Cmp(
799                    if equals { CmpOp::Eq } else { CmpOp::Ne },
800                    actual,
801                    expected,
802                ))
803            }
804        }
805    }
806
807    #[must_use]
808    pub const fn failure_message(self) -> Option<&'static str> {
809        match self {
810            Self::FalseWithMessage { message } => Some(message),
811            Self::True
812            | Self::False
813            | Self::Invoke { .. }
814            | Self::LookaheadTextEquals { .. }
815            | Self::LookaheadNotEquals { .. }
816            | Self::TokenPairAdjacent
817            | Self::ContextChildRuleTextNotEquals { .. }
818            | Self::LocalIntEquals { .. }
819            | Self::LocalIntLessOrEqual { .. }
820            | Self::MemberModuloEquals { .. }
821            | Self::MemberEquals { .. } => None,
822        }
823    }
824}
825
826fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
827    let local = ir.expr(PExpr::LocalArg);
828    let absent = ir.expr(PExpr::IsNull(local));
829    let expected = ir.expr(PExpr::Int(value));
830    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
831    ir.expr(PExpr::Or([absent, comparison].into()))
832}
833
834/// Policy for semantic predicate coordinates that have no runtime
835/// implementation.
836///
837/// ANTLR grammars may embed target-language predicates that the metadata
838/// generator could not translate into a [`ParserPredicate`] table entry. When
839/// recognition reaches such a coordinate the runtime cannot know the grammar
840/// author's intent, so the caller chooses how to proceed.
841///
842/// The default is [`Self::AssumeTrue`], matching the historical behavior of
843/// this runtime. That default is deprecated and will change to [`Self::Error`]
844/// in a future minor release; grammars relying on unconditional predicates
845/// should opt in explicitly.
846#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
847pub enum UnknownSemanticPolicy {
848    /// Treat the predicate as passing, as if it were absent from the grammar.
849    #[default]
850    AssumeTrue,
851    /// Treat the predicate as failing, removing the guarded alternative.
852    AssumeFalse,
853    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
854    /// coordinate that recognition evaluated.
855    Error,
856}
857
858/// Resolves a predicate coordinate that neither a translated table entry nor a
859/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
860///
861/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
862/// so the parse entry can surface every unresolved coordinate afterwards. Both
863/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
864/// funnel through here so a missing implementation is never silently coerced
865/// to a boolean (design goal G1: never silently mis-parse).
866fn apply_unknown_predicate_policy(
867    policy: UnknownSemanticPolicy,
868    rule_index: usize,
869    pred_index: usize,
870    hits: &mut Vec<(usize, usize)>,
871) -> bool {
872    match policy {
873        UnknownSemanticPolicy::AssumeTrue => true,
874        UnknownSemanticPolicy::AssumeFalse => false,
875        UnknownSemanticPolicy::Error => {
876            let coordinate = (rule_index, pred_index);
877            if !hits.contains(&coordinate) {
878                hits.push(coordinate);
879            }
880            false
881        }
882    }
883}
884
885/// Interval-set of expected token types, displayable through a vocabulary —
886/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
887/// generated test actions.
888#[derive(Clone, Debug, Eq, PartialEq)]
889pub struct ExpectedTokenSet {
890    symbols: BTreeSet<i32>,
891}
892
893impl ExpectedTokenSet {
894    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
895    #[must_use]
896    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
897        expected_symbols_display(&self.symbols, vocabulary)
898    }
899}
900
901/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
902///
903/// The first syntax error aborts the parse instead of recovering. Generated
904/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
905#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
906pub struct BailErrorStrategy;
907
908impl BailErrorStrategy {
909    #[must_use]
910    pub const fn new() -> Self {
911        Self
912    }
913}
914
915/// Prediction strategy requested by generated parser harnesses.
916#[derive(Clone, Copy, Debug, Eq, PartialEq)]
917pub enum PredictionMode {
918    /// Prefer the clean full-context outcome when alternatives reach the same
919    /// input position.
920    Ll,
921    /// Preserve SLL's first-viable alternative bias at a decision, even when a
922    /// later full-context alternative could avoid recovery.
923    Sll,
924    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
925    LlExactAmbigDetection,
926}
927
928/// Integer argument metadata for a generated parser rule invocation.
929///
930/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
931/// the generator records the rule-transition source state and the value that
932/// should be visible to semantic predicates inside the callee.
933#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
934pub struct ParserRuleArg {
935    /// ATN state containing the rule transition that receives this argument.
936    pub source_state: usize,
937    /// Callee rule index for the transition.
938    pub rule_index: usize,
939    /// Literal fallback value to expose in the callee.
940    pub value: i64,
941    /// Whether the callee should inherit the caller's current integer argument.
942    pub inherit_local: bool,
943}
944
945/// Integer member mutation attached to an ATN action transition.
946#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
947pub struct ParserMemberAction {
948    /// ATN state containing the action transition.
949    pub source_state: usize,
950    /// Generator-assigned integer member id.
951    pub member: usize,
952    /// Delta applied when the action is reached on one speculative path.
953    pub delta: i64,
954}
955
956/// Integer return-value assignment attached to an ATN action transition.
957///
958/// Generated parsers use this metadata when target actions assign a simple
959/// return field such as `$y=1000;`. The interpreter applies it while selecting
960/// the recognized path so the finished parse tree can answer later
961/// `$label.y` action templates.
962#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
963pub struct ParserReturnAction {
964    /// ATN state containing the action transition.
965    pub source_state: usize,
966    /// Rule index recorded by the serialized action transition.
967    pub rule_index: usize,
968    /// Return-field name as it appears in the grammar.
969    pub name: &'static str,
970    /// Literal integer value assigned by the action.
971    pub value: i64,
972}
973
974impl ParserMemberAction {
975    /// Lowers this speculative member mutation into a `SemIR` action.
976    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
977        let delta = ir.expr(PExpr::Int(self.delta));
978        ParserSemanticAction {
979            source_state: self.source_state,
980            rule_index: usize::MAX,
981            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
982            speculative: true,
983        }
984    }
985}
986
987impl ParserReturnAction {
988    /// Lowers this committed return-value assignment into a `SemIR` action.
989    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
990        let name = ir.intern(self.name);
991        let value = ir.expr(PExpr::Int(self.value));
992        ParserSemanticAction {
993            source_state: self.source_state,
994            rule_index: self.rule_index,
995            stmt: ir.stmt(AStmt::SetReturn(name, value)),
996            speculative: false,
997        }
998    }
999}
1000
1001/// Parser predicate coordinate lowered into [`SemIr`].
1002#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1003pub struct ParserSemanticPredicate {
1004    /// Serialized rule index that owns this predicate.
1005    pub rule_index: usize,
1006    /// Predicate index inside the owning rule.
1007    pub pred_index: usize,
1008    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1009    pub expr: ExprId,
1010    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1011    pub failure_message: Option<&'static str>,
1012}
1013
1014/// Parser action coordinate lowered into [`SemIr`].
1015#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1016pub struct ParserSemanticAction {
1017    /// ATN state containing the action transition.
1018    pub source_state: usize,
1019    /// Serialized rule index recorded by the action transition.
1020    pub rule_index: usize,
1021    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1022    pub stmt: StmtId,
1023    /// Whether this action may run on speculative recognition paths.
1024    pub speculative: bool,
1025}
1026
1027/// Data-driven semantic tables emitted by generated parsers.
1028///
1029/// This is the runtime representation for issue #9's `SemIR` path. Existing
1030/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1031/// remain accepted as deprecated adapters for generated code produced before
1032/// this table existed.
1033#[derive(Clone, Debug, Default, Eq, PartialEq)]
1034pub struct ParserSemantics {
1035    pub ir: SemIr,
1036    pub predicates: Vec<ParserSemanticPredicate>,
1037    pub actions: Vec<ParserSemanticAction>,
1038}
1039
1040/// Optional generated-runtime metadata for metadata-driven parser execution.
1041#[derive(Clone, Copy, Debug, Default)]
1042pub struct ParserRuntimeOptions<'a> {
1043    /// Rule indexes whose `@init` actions should be replayed.
1044    pub init_action_rules: &'a [usize],
1045    /// Whether generated parse-tree contexts should retain alternative numbers.
1046    pub track_alt_numbers: bool,
1047    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1048    pub predicates: &'a [(usize, usize, ParserPredicate)],
1049    /// `SemIR` predicate/action table emitted by newer generated parsers.
1050    pub semantics: Option<&'a ParserSemantics>,
1051    /// Rule-call integer argument table keyed by ATN source state.
1052    pub rule_args: &'a [ParserRuleArg],
1053    /// Integer member mutations keyed by ATN action source state.
1054    pub member_actions: &'a [ParserMemberAction],
1055    /// Integer return assignments keyed by ATN action source state.
1056    pub return_actions: &'a [ParserReturnAction],
1057    /// How to evaluate semantic predicate coordinates absent from
1058    /// `predicates`.
1059    pub unknown_predicate_policy: UnknownSemanticPolicy,
1060}
1061
1062pub trait Parser: Recognizer {
1063    /// Reports whether generated parser rules should build parse-tree nodes
1064    /// while recognizing input.
1065    fn build_parse_trees(&self) -> bool;
1066
1067    /// Enables or disables parse-tree construction for subsequent rule calls.
1068    fn set_build_parse_trees(&mut self, build: bool);
1069
1070    /// Returns the number of parser syntax errors recorded by committed parse
1071    /// paths so far.
1072    fn number_of_syntax_errors(&self) -> usize {
1073        0
1074    }
1075
1076    /// Reports whether prediction diagnostic-listener messages are emitted
1077    /// during parser ATN recognition.
1078    fn report_diagnostic_errors(&self) -> bool {
1079        false
1080    }
1081
1082    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1083    /// rule calls.
1084    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1085
1086    /// Reports the prediction strategy used when selecting among alternatives.
1087    fn prediction_mode(&self) -> PredictionMode {
1088        PredictionMode::Ll
1089    }
1090
1091    /// Sets the prediction strategy for subsequent rule calls.
1092    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1093}
1094
1095#[derive(Debug)]
1096struct LeftRecursiveCallerOverlap {
1097    atn_key: SharedAtnCacheKey,
1098    state_number: usize,
1099    symbol: i32,
1100    context_version: usize,
1101    overlaps: bool,
1102}
1103
1104const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1105
1106#[derive(Debug)]
1107pub struct BaseParser<S, H = NoSemanticHooks> {
1108    input: CommonTokenStream<S>,
1109    tree: ParseTreeStorage,
1110    data: RecognizerData,
1111    semantic_hooks: H,
1112    build_parse_trees: bool,
1113    syntax_errors: usize,
1114    report_diagnostic_errors: bool,
1115    prediction_mode: PredictionMode,
1116    prediction_diagnostics: Vec<ParserDiagnostic>,
1117    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1118    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1119    generated_sync_expected: Option<TokenBitSet>,
1120    int_members: BTreeMap<usize, i64>,
1121    rule_context_stack: Vec<RuleContextFrame>,
1122    rule_context_version: usize,
1123    left_recursive_caller_overlap_cache:
1124        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1125    pending_invoking_states: Vec<isize>,
1126    precedence_stack: Vec<i32>,
1127    /// Predicate side effects are observable in a few target-template tests;
1128    /// speculative recognition may revisit the same coordinate, so replay it
1129    /// once per parser instance.
1130    invoked_predicates: Vec<(usize, usize)>,
1131    /// Bail error strategy: the first syntax error aborts the parse instead of
1132    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1133    /// through `set_error_handler(BailErrorStrategy::new())`.
1134    bail_on_error: bool,
1135    /// How to evaluate predicate coordinates missing from the active
1136    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1137    unknown_predicate_policy: UnknownSemanticPolicy,
1138    /// Unknown predicate coordinates evaluated by the current parse, recorded
1139    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1140    unknown_predicate_hits: Vec<(usize, usize)>,
1141    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1142    /// that no hook handled, recorded so a generated `hook`/error-disposed
1143    /// action fails loud instead of being silently dropped. Keyed by
1144    /// `(rule_index, source_state)`.
1145    unhandled_action_hits: Vec<(usize, usize)>,
1146    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1147    /// hot rule-transition checks to a vector lookup after the first visit
1148    /// while the thread-local shared ATN cache still owns the cross-parse
1149    /// computed value.
1150    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1151    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1152    /// epsilon-reachable consuming transition and shows up as a hot loop in
1153    /// `next_recovery_context` and recovery diagnostics on long inputs.
1154    /// Keying on `state_number` and sharing the result through `Rc` removes
1155    /// repeated DFS plus per-call `BTreeSet` allocations.
1156    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1157    /// Same expected-symbol cache as a bitset for generated parser sync.
1158    /// Successful parses only need `contains` and union; keeping that path out
1159    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1160    /// check and defers deterministic formatting to diagnostics.
1161    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1162    /// Per-state cache for whether a return state can finish its owning rule
1163    /// without consuming more input. Generated-parser sync uses this to walk
1164    /// parent prediction contexts for nullable exits without paying repeated
1165    /// epsilon-closure searches on every loop or optional decision.
1166    rule_stop_reach_cache: Vec<Option<bool>>,
1167    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1168    /// threads the same epsilon-recovery context through hundreds of follow
1169    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1170    /// reference bump and lets the memo key hash by pointer.
1171    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1172    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1173    /// touch a given decision state still pay no upfront cost; once cached,
1174    /// the recognizer prunes alternatives whose look-1 cannot accept the
1175    /// current lookahead, letting common SLL decisions reduce to a single
1176    /// transition walk instead of a full speculative fan-out.
1177    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1178    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1179    /// Each multi-trans visit asks "given this decision state and this
1180    /// lookahead token, which alt do I commit to?" Hitting this cache
1181    /// turns the question into a hashmap probe instead of re-scanning
1182    /// the decision's per-transition FIRST sets every visit.
1183    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1184    /// Predicate results shared by the fast recognizer's clean and recovery
1185    /// attempts. The eligible fast path keeps every runtime-provided input
1186    /// fixed, and custom predicate hooks are required to be replay-safe.
1187    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1188    /// Cache for whether an ATN state can reach itself without consuming
1189    /// input. Only those states need the recursive recognizer's
1190    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1191    /// grammar-static cache survive parser resets without reusing state
1192    /// coordinates after the parser is driven against a different ATN.
1193    empty_cycle_cache: Vec<Option<bool>>,
1194    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1195    /// Probe state for deciding whether clean-pass memo entries are worth
1196    /// storing for the current parse.
1197    clean_memo_mode: CleanMemoMode,
1198    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1199    clean_memo_probe_samples: usize,
1200    clean_memo_probe_repeats: usize,
1201    clean_memo_sparse_samples: usize,
1202    /// Reusable cycle and memo storage for one top-level fast recognition.
1203    fast_recognize_scratch: FastRecognizeTopScratch,
1204    /// Reusable direct-index/hash storage for clean speculative endpoints.
1205    fast_outcome_dedup: FastOutcomeDedupScratch,
1206    /// Empty recovery-symbols singleton used as the default at rule entry and
1207    /// after token consumption.
1208    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1209    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1210    /// prefilter trims speculative rule calls whose called rule cannot
1211    /// match the current lookahead, but it also bypasses single-token
1212    /// insertion / deletion recovery that ANTLR runs at the rule's first
1213    /// consuming transition. `parse_atn_rule` flips this off and retries
1214    /// when the first pass produces no clean outcome so the runtime can
1215    /// repair inputs the reference parser would have repaired.
1216    fast_first_set_prefilter: bool,
1217    /// Whether the fast recognizer should explore parser error-recovery paths.
1218    /// Public rule parsing starts with this disabled for the common valid-input
1219    /// path and enables it only for the retry that needs ANTLR-style repairs.
1220    fast_recovery_enabled: bool,
1221    /// Whether the fast recognizer should record terminal-token nodes while
1222    /// speculating. Clean valid-input parsing can reconstruct terminals from
1223    /// selected rule spans after recognition, avoiding many speculative
1224    /// nodes that are thrown away with losing paths.
1225    fast_token_nodes_enabled: bool,
1226    /// Parser-owned append-only storage for speculative recognition output.
1227    /// Each public interpreted-rule entry clears lengths while retaining
1228    /// bounded backing capacities for parser reuse.
1229    recognition_arena: RecognitionArena,
1230    last_recognition_arena_root: NodeSeqId,
1231    last_recognition_arena_diagnostics: DiagnosticSeqId,
1232}
1233
1234/// Rollback marker for speculative generated parser paths.
1235#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1236pub struct GeneratedDiagnosticsCheckpoint {
1237    diagnostics_len: usize,
1238    syntax_errors: usize,
1239    tree: ParseTreeCheckpoint,
1240}
1241
1242/// Storage and reachability counters for the most recent interpreted-rule
1243/// recognition arena.
1244#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1245pub struct RecognitionArenaStats {
1246    pub total_nodes: usize,
1247    pub live_nodes: usize,
1248    pub dead_nodes: usize,
1249    pub node_capacity: usize,
1250    pub total_links: usize,
1251    pub live_links: usize,
1252    pub dead_links: usize,
1253    pub link_capacity: usize,
1254    pub total_extras: usize,
1255    pub live_extras: usize,
1256    pub dead_extras: usize,
1257    pub extra_capacity: usize,
1258}
1259
1260#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1261struct RuleContextFrame {
1262    rule_index: usize,
1263    invoking_state: isize,
1264}
1265
1266#[derive(Clone, Debug, Eq, PartialEq)]
1267struct RecognizeOutcome {
1268    index: usize,
1269    consumed_eof: bool,
1270    alt_number: usize,
1271    member_values: BTreeMap<usize, i64>,
1272    return_values: BTreeMap<String, i64>,
1273    diagnostics: DiagnosticSeqId,
1274    decisions: Vec<usize>,
1275    actions: Vec<ParserAction>,
1276    nodes: NodeSeqId,
1277}
1278
1279#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1280struct FastRecognizeOutcome {
1281    index: usize,
1282    consumed_eof: bool,
1283    diagnostics: DiagnosticSeqId,
1284    deferred_nodes: FastDeferredNodeId,
1285    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1286    /// Copying an outcome copies this compact ID; prepending appends one
1287    /// `SeqLink` without allocating an individual node or list tail.
1288    nodes: NodeSeqId,
1289}
1290
1291#[derive(Debug, Default)]
1292struct FastRecognizeTopScratch {
1293    visiting: FxHashSet<FastRecognizeKey>,
1294    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1295}
1296
1297impl FastRecognizeTopScratch {
1298    fn prepare(&mut self, memo_capacity: usize) {
1299        self.visiting.clear();
1300        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1301        self.memo.clear();
1302        self.memo.reserve(memo_capacity);
1303    }
1304
1305    fn release_oversized_memo(&mut self) {
1306        self.memo.clear();
1307        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1308            self.memo = FxHashMap::default();
1309        }
1310    }
1311}
1312
1313fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1314    buffered_tokens.saturating_mul(8).clamp(
1315        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1316        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1317    )
1318}
1319
1320#[derive(Debug, Default)]
1321struct FastOutcomeDedupScratch {
1322    dense_words: Vec<u64>,
1323    touched_dense_words: Vec<u32>,
1324    sparse_keys: FxHashSet<(usize, bool)>,
1325}
1326
1327/// Handle into the parser-owned deferred tree rope.
1328///
1329/// The sentinel keeps outcomes and repetition paths compact without an
1330/// `Option` discriminant or per-node reference counting.
1331#[repr(transparent)]
1332#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1333struct FastDeferredNodeId(u32);
1334
1335impl FastDeferredNodeId {
1336    const EMPTY: Self = Self(u32::MAX);
1337
1338    const fn is_empty(self) -> bool {
1339        self.0 == Self::EMPTY.0
1340    }
1341}
1342
1343impl Default for FastDeferredNodeId {
1344    fn default() -> Self {
1345        Self::EMPTY
1346    }
1347}
1348
1349#[repr(transparent)]
1350#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1351struct FastDeferredRuleId(u32);
1352
1353/// One immutable deferred-tree rope record in `RecognitionArena`.
1354#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1355enum FastDeferredNode {
1356    Fragment(NodeSeqId),
1357    Rule(FastDeferredRuleId),
1358    Concat {
1359        prefix: FastDeferredNodeId,
1360        suffix: FastDeferredNodeId,
1361    },
1362}
1363
1364#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1365struct FastDeferredRule {
1366    rule_index: u32,
1367    invoking_state: i32,
1368    start_index: u32,
1369    stop_index: Option<u32>,
1370    deferred_children: FastDeferredNodeId,
1371    children: NodeSeqId,
1372}
1373
1374#[repr(transparent)]
1375#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1376struct RecognizedNodeId(u32);
1377
1378#[repr(transparent)]
1379#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1380struct NodeSeqId(u32);
1381
1382impl NodeSeqId {
1383    const EMPTY: Self = Self(u32::MAX);
1384
1385    const fn is_empty(self) -> bool {
1386        self.0 == Self::EMPTY.0
1387    }
1388}
1389
1390impl Default for NodeSeqId {
1391    fn default() -> Self {
1392        Self::EMPTY
1393    }
1394}
1395
1396#[repr(transparent)]
1397#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1398struct DiagnosticSeqId(u32);
1399
1400impl DiagnosticSeqId {
1401    const EMPTY: Self = Self(u32::MAX);
1402
1403    const fn is_empty(self) -> bool {
1404        self.0 == Self::EMPTY.0
1405    }
1406}
1407
1408impl Default for DiagnosticSeqId {
1409    fn default() -> Self {
1410        Self::EMPTY
1411    }
1412}
1413
1414#[repr(transparent)]
1415#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1416struct RecognitionExtraId(u32);
1417
1418#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1419struct SeqLink {
1420    head: RecognizedNodeId,
1421    tail: NodeSeqId,
1422}
1423
1424#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1425struct DiagnosticLink {
1426    head: RecognitionExtraId,
1427    tail: DiagnosticSeqId,
1428}
1429
1430struct ArenaRuleSpec {
1431    rule_index: usize,
1432    invoking_state: isize,
1433    alt_number: usize,
1434    start_index: usize,
1435    stop_index: Option<usize>,
1436    return_values: BTreeMap<String, i64>,
1437    children: NodeSeqId,
1438}
1439
1440/// Compact speculative node record. Common records contain only IDs and
1441/// scalars; missing-token text and generated return values live in `extras`.
1442#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1443enum ArenaRecognizedNode {
1444    Token {
1445        token: TokenId,
1446    },
1447    ErrorToken {
1448        token: TokenId,
1449    },
1450    MissingToken {
1451        extra: RecognitionExtraId,
1452    },
1453    Rule {
1454        rule_index: u32,
1455        invoking_state: i32,
1456        alt_number: u32,
1457        start_index: u32,
1458        stop_index: Option<u32>,
1459        return_values: Option<RecognitionExtraId>,
1460        children: NodeSeqId,
1461    },
1462    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1463    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1464    /// public rule entry hands the tree to the caller.
1465    LeftRecursiveBoundary {
1466        rule_index: u32,
1467    },
1468}
1469
1470#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1471enum RecognitionExtra {
1472    MissingToken {
1473        token_type: i32,
1474        at_index: u32,
1475        text: String,
1476    },
1477    ReturnValues(BTreeMap<String, i64>),
1478    Diagnostic(ParserDiagnostic),
1479}
1480
1481#[derive(Debug, Default)]
1482struct RecognitionArena {
1483    nodes: Vec<ArenaRecognizedNode>,
1484    seq_links: Vec<SeqLink>,
1485    diagnostic_links: Vec<DiagnosticLink>,
1486    extras: Vec<RecognitionExtra>,
1487    deferred_nodes: Vec<FastDeferredNode>,
1488    deferred_rules: Vec<FastDeferredRule>,
1489}
1490
1491// Preserve normal parser reuse while preventing one pathological parse from
1492// pinning an arbitrarily large arena for the parser's remaining lifetime.
1493const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1494const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1495const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1496const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1497const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1498const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1499
1500impl RecognitionArena {
1501    fn reset(&mut self) {
1502        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1503        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1504        reset_arena_vec(
1505            &mut self.diagnostic_links,
1506            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1507        );
1508        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1509        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1510        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1511    }
1512
1513    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1514        let id = RecognizedNodeId(
1515            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1516        );
1517        self.nodes.push(node);
1518        id
1519    }
1520
1521    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1522        let id = RecognitionExtraId(
1523            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1524        );
1525        self.extras.push(extra);
1526        id
1527    }
1528
1529    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1530        let id = NodeSeqId(
1531            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1532        );
1533        self.seq_links.push(SeqLink { head, tail });
1534        id
1535    }
1536
1537    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1538        let id = FastDeferredNodeId(
1539            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1540        );
1541        self.deferred_nodes.push(node);
1542        id
1543    }
1544
1545    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1546        let id = FastDeferredRuleId(
1547            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1548        );
1549        self.deferred_rules.push(rule);
1550        id
1551    }
1552
1553    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1554        if nodes.is_empty() {
1555            FastDeferredNodeId::EMPTY
1556        } else {
1557            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1558        }
1559    }
1560
1561    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1562        let rule = self.push_deferred_rule(rule);
1563        self.push_deferred_node(FastDeferredNode::Rule(rule))
1564    }
1565
1566    fn concat_deferred_nodes(
1567        &mut self,
1568        prefix: FastDeferredNodeId,
1569        suffix: FastDeferredNodeId,
1570    ) -> FastDeferredNodeId {
1571        if prefix.is_empty() {
1572            return suffix;
1573        }
1574        if suffix.is_empty() {
1575            return prefix;
1576        }
1577        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1578    }
1579
1580    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1581        self.deferred_nodes[id.0 as usize]
1582    }
1583
1584    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1585        self.deferred_rules[id.0 as usize]
1586    }
1587
1588    fn prepend_diagnostic(
1589        &mut self,
1590        tail: DiagnosticSeqId,
1591        diagnostic: ParserDiagnostic,
1592    ) -> DiagnosticSeqId {
1593        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1594        self.prepend_diagnostic_id(tail, head)
1595    }
1596
1597    fn prepend_diagnostic_id(
1598        &mut self,
1599        tail: DiagnosticSeqId,
1600        head: RecognitionExtraId,
1601    ) -> DiagnosticSeqId {
1602        let id = DiagnosticSeqId(
1603            u32::try_from(self.diagnostic_links.len())
1604                .expect("diagnostic sequence arena fits in u32"),
1605        );
1606        self.diagnostic_links.push(DiagnosticLink { head, tail });
1607        id
1608    }
1609
1610    fn concat_diagnostics(
1611        &mut self,
1612        prefix: DiagnosticSeqId,
1613        mut suffix: DiagnosticSeqId,
1614    ) -> DiagnosticSeqId {
1615        if prefix.is_empty() {
1616            return suffix;
1617        }
1618        if suffix.is_empty() {
1619            return prefix;
1620        }
1621        let mut reversed = DiagnosticSeqId::EMPTY;
1622        let mut cursor = prefix;
1623        while let Some(link) = self.diagnostic_link(cursor) {
1624            reversed = self.prepend_diagnostic_id(reversed, link.head);
1625            cursor = link.tail;
1626        }
1627        while let Some(link) = self.diagnostic_link(reversed) {
1628            suffix = self.prepend_diagnostic_id(suffix, link.head);
1629            reversed = link.tail;
1630        }
1631        suffix
1632    }
1633
1634    #[cfg(test)]
1635    fn diagnostic_sequence(
1636        &mut self,
1637        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1638    ) -> DiagnosticSeqId {
1639        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1640        let mut sequence = DiagnosticSeqId::EMPTY;
1641        for diagnostic in diagnostics.into_iter().rev() {
1642            sequence = self.prepend_diagnostic(sequence, diagnostic);
1643        }
1644        sequence
1645    }
1646
1647    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1648        self.nodes[id.0 as usize]
1649    }
1650
1651    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1652        &self.extras[id.0 as usize]
1653    }
1654
1655    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1656        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1657    }
1658
1659    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1660        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1661    }
1662
1663    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1664        NodeSeqIter {
1665            arena: self,
1666            cursor: sequence,
1667        }
1668    }
1669
1670    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1671        DiagnosticSeqIter {
1672            arena: self,
1673            cursor: sequence,
1674        }
1675    }
1676
1677    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1678        self.diagnostics(sequence).count()
1679    }
1680
1681    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1682        self.diagnostics(sequence)
1683            .filter(|diagnostic| {
1684                diagnostic.message.starts_with("mismatched input ")
1685                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1686            })
1687            .count()
1688    }
1689
1690    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1691        self.diagnostics(left).cmp(self.diagnostics(right))
1692    }
1693
1694    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1695        self.iter(sequence).count()
1696    }
1697
1698    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1699        self.iter(sequence).any(|node| match self.node(node) {
1700            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1701            ArenaRecognizedNode::Rule { children, .. } => {
1702                self.sequence_has_left_recursive_boundary(children)
1703            }
1704            ArenaRecognizedNode::Token { .. }
1705            | ArenaRecognizedNode::ErrorToken { .. }
1706            | ArenaRecognizedNode::MissingToken { .. } => false,
1707        })
1708    }
1709
1710    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1711        self.iter(sequence).any(|node| {
1712            matches!(
1713                self.node(node),
1714                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1715            )
1716        })
1717    }
1718
1719    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1720        self.iter(sequence).any(|node| {
1721            matches!(
1722                self.node(node),
1723                ArenaRecognizedNode::Token { .. }
1724                    | ArenaRecognizedNode::ErrorToken { .. }
1725                    | ArenaRecognizedNode::MissingToken { .. }
1726            )
1727        })
1728    }
1729
1730    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1731        match self.node(node) {
1732            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1733                Some(token.index())
1734            }
1735            ArenaRecognizedNode::MissingToken { extra } => {
1736                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1737                    unreachable!("missing-token node must reference missing-token extra");
1738                };
1739                Some(*at_index as usize)
1740            }
1741            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1742            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1743        }
1744    }
1745
1746    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
1747        match self.node(node) {
1748            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1749                Some(token.index())
1750            }
1751            ArenaRecognizedNode::MissingToken { extra } => {
1752                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1753                    unreachable!("missing-token node must reference missing-token extra");
1754                };
1755                (*at_index as usize).checked_sub(1)
1756            }
1757            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
1758            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1759        }
1760    }
1761
1762    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
1763        let start = self.node_start_index(node)?;
1764        let stop = self.node_stop_index(node);
1765        Some((start, stop))
1766    }
1767
1768    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
1769        self.iter(sequence)
1770            .find_map(|node| self.node_start_index(node))
1771    }
1772
1773    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
1774        let mut stop = None;
1775        for node in self.iter(sequence) {
1776            if let Some(index) = self.node_stop_index(node) {
1777                stop = Some(index);
1778            }
1779        }
1780        stop
1781    }
1782
1783    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
1784        self.iter(sequence)
1785            .any(|node| self.node_needs_stable_tie(node))
1786    }
1787
1788    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
1789        match self.node(node) {
1790            ArenaRecognizedNode::Token { .. }
1791            | ArenaRecognizedNode::ErrorToken { .. }
1792            | ArenaRecognizedNode::MissingToken { .. } => false,
1793            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1794            ArenaRecognizedNode::Rule {
1795                rule_index,
1796                children,
1797                ..
1798            } => self.iter(children).any(|child| {
1799                matches!(
1800                    self.node(child),
1801                    ArenaRecognizedNode::Rule {
1802                        rule_index: child_rule,
1803                        ..
1804                    } if child_rule == rule_index
1805                ) || self.node_needs_stable_tie(child)
1806            }),
1807        }
1808    }
1809
1810    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
1811        loop {
1812            match (self.link(left), self.link(right)) {
1813                (Some(left_link), Some(right_link)) => {
1814                    let order = self.compare_nodes(left_link.head, right_link.head);
1815                    if order != Ordering::Equal {
1816                        return order;
1817                    }
1818                    left = left_link.tail;
1819                    right = right_link.tail;
1820                }
1821                (None, None) => return Ordering::Equal,
1822                (None, Some(_)) => return Ordering::Less,
1823                (Some(_), None) => return Ordering::Greater,
1824            }
1825        }
1826    }
1827
1828    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
1829        let left = self.node(left);
1830        let right = self.node(right);
1831        match (left, right) {
1832            (
1833                ArenaRecognizedNode::Token { token: left },
1834                ArenaRecognizedNode::Token { token: right },
1835            )
1836            | (
1837                ArenaRecognizedNode::ErrorToken { token: left },
1838                ArenaRecognizedNode::ErrorToken { token: right },
1839            ) => left.cmp(&right),
1840            (
1841                ArenaRecognizedNode::MissingToken { extra: left },
1842                ArenaRecognizedNode::MissingToken { extra: right },
1843            ) => self.extra(left).cmp(self.extra(right)),
1844            (
1845                ArenaRecognizedNode::Rule {
1846                    rule_index: left_rule,
1847                    invoking_state: left_invoking,
1848                    alt_number: left_alt,
1849                    start_index: left_start,
1850                    stop_index: left_stop,
1851                    return_values: left_returns,
1852                    children: left_children,
1853                },
1854                ArenaRecognizedNode::Rule {
1855                    rule_index: right_rule,
1856                    invoking_state: right_invoking,
1857                    alt_number: right_alt,
1858                    start_index: right_start,
1859                    stop_index: right_stop,
1860                    return_values: right_returns,
1861                    children: right_children,
1862                },
1863            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
1864                .cmp(&(
1865                    right_rule,
1866                    right_invoking,
1867                    right_alt,
1868                    right_start,
1869                    right_stop,
1870                ))
1871                .then_with(|| {
1872                    left_returns
1873                        .map(|id| self.extra(id))
1874                        .cmp(&right_returns.map(|id| self.extra(id)))
1875                })
1876                .then_with(|| self.compare_sequences(left_children, right_children)),
1877            (
1878                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: left },
1879                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: right },
1880            ) => left.cmp(&right),
1881            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
1882        }
1883    }
1884
1885    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1886        let mut reversed = NodeSeqId::EMPTY;
1887        while let Some(link) = self.link(sequence) {
1888            reversed = self.prepend(reversed, link.head);
1889            sequence = link.tail;
1890        }
1891        reversed
1892    }
1893
1894    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1895        if !self.sequence_has_direct_boundary(sequence) {
1896            return sequence;
1897        }
1898        let mut reversed = NodeSeqId::EMPTY;
1899        while let Some(link) = self.link(sequence) {
1900            match self.node(link.head) {
1901                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
1902                    if !reversed.is_empty() {
1903                        let children = self.reverse_sequence(reversed);
1904                        let start_index = self.sequence_start_index(children).unwrap_or_default();
1905                        let stop_index = self.sequence_stop_index(children);
1906                        let rule = self.push_node(ArenaRecognizedNode::Rule {
1907                            rule_index,
1908                            invoking_state: -1,
1909                            alt_number: 0,
1910                            start_index: u32::try_from(start_index)
1911                                .expect("left-recursive start index fits in u32"),
1912                            stop_index: stop_index.map(|index| {
1913                                u32::try_from(index).expect("left-recursive stop index fits in u32")
1914                            }),
1915                            return_values: None,
1916                            children,
1917                        });
1918                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
1919                    }
1920                }
1921                _ => {
1922                    reversed = self.prepend(reversed, link.head);
1923                }
1924            }
1925            sequence = link.tail;
1926        }
1927        self.reverse_sequence(reversed)
1928    }
1929
1930    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
1931        let mut live_nodes = vec![false; self.nodes.len()];
1932        let mut live_links = vec![false; self.seq_links.len()];
1933        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
1934        let mut live_extras = vec![false; self.extras.len()];
1935        let mut pending = vec![root];
1936        while let Some(mut sequence) = pending.pop() {
1937            while let Some(link) = self.link(sequence) {
1938                let link_index = sequence.0 as usize;
1939                if live_links[link_index] {
1940                    break;
1941                }
1942                live_links[link_index] = true;
1943                let node_index = link.head.0 as usize;
1944                if !live_nodes[node_index] {
1945                    live_nodes[node_index] = true;
1946                    match self.node(link.head) {
1947                        ArenaRecognizedNode::MissingToken { extra } => {
1948                            live_extras[extra.0 as usize] = true;
1949                        }
1950                        ArenaRecognizedNode::Rule {
1951                            return_values,
1952                            children,
1953                            ..
1954                        } => {
1955                            if let Some(extra) = return_values {
1956                                live_extras[extra.0 as usize] = true;
1957                            }
1958                            pending.push(children);
1959                        }
1960                        ArenaRecognizedNode::Token { .. }
1961                        | ArenaRecognizedNode::ErrorToken { .. }
1962                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
1963                    }
1964                }
1965                sequence = link.tail;
1966            }
1967        }
1968        let mut diagnostics = diagnostics;
1969        while let Some(link) = self.diagnostic_link(diagnostics) {
1970            let link_index = diagnostics.0 as usize;
1971            if live_diagnostic_links[link_index] {
1972                break;
1973            }
1974            live_diagnostic_links[link_index] = true;
1975            live_extras[link.head.0 as usize] = true;
1976            diagnostics = link.tail;
1977        }
1978        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
1979        let live_link_count = live_links.into_iter().filter(|live| *live).count()
1980            + live_diagnostic_links
1981                .into_iter()
1982                .filter(|live| *live)
1983                .count();
1984        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
1985        let total_links = self.seq_links.len() + self.diagnostic_links.len();
1986        RecognitionArenaStats {
1987            total_nodes: self.nodes.len(),
1988            live_nodes: live_node_count,
1989            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
1990            node_capacity: self.nodes.capacity(),
1991            total_links,
1992            live_links: live_link_count,
1993            dead_links: total_links.saturating_sub(live_link_count),
1994            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
1995            total_extras: self.extras.len(),
1996            live_extras: live_extra_count,
1997            dead_extras: self.extras.len().saturating_sub(live_extra_count),
1998            extra_capacity: self.extras.capacity(),
1999        }
2000    }
2001}
2002
2003fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
2004    if storage.capacity() > max_retained_capacity {
2005        *storage = Vec::new();
2006    } else {
2007        storage.clear();
2008    }
2009}
2010
2011const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2012    match node {
2013        ArenaRecognizedNode::Token { .. } => 0,
2014        ArenaRecognizedNode::ErrorToken { .. } => 1,
2015        ArenaRecognizedNode::MissingToken { .. } => 2,
2016        ArenaRecognizedNode::Rule { .. } => 3,
2017        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2018    }
2019}
2020
2021struct NodeSeqIter<'a> {
2022    arena: &'a RecognitionArena,
2023    cursor: NodeSeqId,
2024}
2025
2026impl Iterator for NodeSeqIter<'_> {
2027    type Item = RecognizedNodeId;
2028
2029    fn next(&mut self) -> Option<Self::Item> {
2030        let link = self.arena.link(self.cursor)?;
2031        self.cursor = link.tail;
2032        Some(link.head)
2033    }
2034}
2035
2036struct DiagnosticSeqIter<'a> {
2037    arena: &'a RecognitionArena,
2038    cursor: DiagnosticSeqId,
2039}
2040
2041impl<'a> Iterator for DiagnosticSeqIter<'a> {
2042    type Item = &'a ParserDiagnostic;
2043
2044    fn next(&mut self) -> Option<Self::Item> {
2045        let link = self.arena.diagnostic_link(self.cursor)?;
2046        self.cursor = link.tail;
2047        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2048            unreachable!("diagnostic link must reference diagnostic extra");
2049        };
2050        Some(diagnostic)
2051    }
2052}
2053
2054#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2055struct ParserDiagnostic {
2056    line: usize,
2057    column: usize,
2058    message: String,
2059}
2060
2061#[derive(Clone, Debug, Default, Eq, PartialEq)]
2062struct ExpectedTokens {
2063    index: Option<usize>,
2064    symbols: BTreeSet<i32>,
2065    no_viable: Option<NoViableAlternative>,
2066}
2067
2068#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2069struct NoViableAlternative {
2070    start_index: usize,
2071    error_index: usize,
2072}
2073
2074impl ExpectedTokens {
2075    /// Records the expected symbols for the farthest token index reached by any
2076    /// failed ATN path.
2077    fn record_transition(
2078        &mut self,
2079        index: usize,
2080        transition: ParserTransition<'_>,
2081        max_token_type: i32,
2082    ) {
2083        let symbols = transition_expected_symbols(transition, max_token_type);
2084        match self.index {
2085            Some(current) if index < current => {}
2086            Some(current) if index == current => self.symbols.extend(symbols),
2087            _ => {
2088                self.index = Some(index);
2089                self.symbols = symbols;
2090            }
2091        }
2092    }
2093
2094    /// Records an ambiguous decision that failed after consuming a shared
2095    /// prefix, which ANTLR reports as `no viable alternative`.
2096    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2097        match self.no_viable {
2098            Some(current) if error_index < current.error_index => {}
2099            _ => {
2100                self.no_viable = Some(NoViableAlternative {
2101                    start_index,
2102                    error_index,
2103                });
2104            }
2105        }
2106    }
2107}
2108
2109/// Compact token-type set for parser-internal FIRST/lookahead caches.
2110///
2111/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2112/// but the hot recognizer path mostly needs `contains` and set union over
2113/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2114/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2115#[derive(Clone, Debug, Default, Eq, PartialEq)]
2116struct TokenBitSet {
2117    words: Vec<u64>,
2118}
2119
2120impl TokenBitSet {
2121    fn insert(&mut self, symbol: i32) {
2122        let Some(slot) = token_bit_slot(symbol) else {
2123            return;
2124        };
2125        let word = slot / u64::BITS as usize;
2126        if word >= self.words.len() {
2127            self.words.resize(word + 1, 0);
2128        }
2129        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2130    }
2131
2132    fn extend_range(&mut self, start: i32, stop: i32) {
2133        let (start, stop) = if start <= stop {
2134            (start, stop)
2135        } else {
2136            (stop, start)
2137        };
2138        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2139            self.insert(TOKEN_EOF);
2140        }
2141        let positive_start = start.max(1);
2142        if positive_start > stop {
2143            return;
2144        }
2145        let Some(start_slot) = token_bit_slot(positive_start) else {
2146            return;
2147        };
2148        let Some(stop_slot) = token_bit_slot(stop) else {
2149            return;
2150        };
2151        self.extend_slot_range(start_slot, stop_slot);
2152    }
2153
2154    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2155        if start_slot > stop_slot {
2156            return;
2157        }
2158        let start_word = start_slot / u64::BITS as usize;
2159        let stop_word = stop_slot / u64::BITS as usize;
2160        if stop_word >= self.words.len() {
2161            self.words.resize(stop_word + 1, 0);
2162        }
2163        let start_offset = start_slot % u64::BITS as usize;
2164        let stop_offset = stop_slot % u64::BITS as usize;
2165        if start_word == stop_word {
2166            self.words[start_word] |=
2167                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2168            return;
2169        }
2170        self.words[start_word] |= !0_u64 << start_offset;
2171        for word in &mut self.words[(start_word + 1)..stop_word] {
2172            *word = !0_u64;
2173        }
2174        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2175    }
2176
2177    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2178        for symbol in symbols {
2179            self.insert(symbol);
2180        }
2181    }
2182
2183    fn extend_from(&mut self, other: &Self) {
2184        if other.words.len() > self.words.len() {
2185            self.words.resize(other.words.len(), 0);
2186        }
2187        for (left, right) in self.words.iter_mut().zip(&other.words) {
2188            *left |= *right;
2189        }
2190    }
2191
2192    fn contains(&self, symbol: i32) -> bool {
2193        let Some(slot) = token_bit_slot(symbol) else {
2194            return false;
2195        };
2196        let word = slot / u64::BITS as usize;
2197        self.words
2198            .get(word)
2199            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2200    }
2201
2202    fn is_empty(&self) -> bool {
2203        self.words.iter().all(|word| *word == 0)
2204    }
2205
2206    fn symbols(&self) -> impl Iterator<Item = i32> + '_ {
2207        self.words
2208            .iter()
2209            .copied()
2210            .enumerate()
2211            .flat_map(|(word_index, mut bits)| {
2212                std::iter::from_fn(move || {
2213                    while bits != 0 {
2214                        let bit = bits.trailing_zeros() as usize;
2215                        bits &= bits - 1;
2216                        if let Some(symbol) =
2217                            token_bit_symbol(word_index * u64::BITS as usize + bit)
2218                        {
2219                            return Some(symbol);
2220                        }
2221                    }
2222                    None
2223                })
2224            })
2225    }
2226
2227    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2228        target.extend(self.symbols());
2229    }
2230
2231    fn to_btree_set(&self) -> BTreeSet<i32> {
2232        let mut out = BTreeSet::new();
2233        self.extend_btree_set(&mut out);
2234        out
2235    }
2236}
2237
2238fn token_bit_slot(symbol: i32) -> Option<usize> {
2239    if symbol == TOKEN_EOF {
2240        Some(0)
2241    } else if symbol > 0 {
2242        usize::try_from(symbol).ok()
2243    } else {
2244        None
2245    }
2246}
2247
2248fn token_bit_symbol(slot: usize) -> Option<i32> {
2249    if slot == 0 {
2250        Some(TOKEN_EOF)
2251    } else {
2252        i32::try_from(slot).ok()
2253    }
2254}
2255
2256/// Converts one consuming transition into the token types that would satisfy it
2257/// for diagnostic reporting.
2258fn transition_expected_symbols(
2259    transition: ParserTransition<'_>,
2260    max_token_type: i32,
2261) -> BTreeSet<i32> {
2262    let mut symbols = BTreeSet::new();
2263    match &transition.data() {
2264        Transition::Atom { label, .. } => {
2265            symbols.insert(*label);
2266        }
2267        Transition::Range { start, stop, .. } => {
2268            symbols.extend(*start..=*stop);
2269        }
2270        Transition::Set { set, .. } => {
2271            for (start, stop) in set.ranges() {
2272                symbols.extend(start..=stop);
2273            }
2274        }
2275        Transition::NotSet { set, .. } => {
2276            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2277        }
2278        Transition::Wildcard { .. } => {
2279            symbols.extend(1..=max_token_type);
2280        }
2281        Transition::Epsilon { .. }
2282        | Transition::Rule { .. }
2283        | Transition::Predicate { .. }
2284        | Transition::Action { .. }
2285        | Transition::Precedence { .. } => {}
2286    }
2287    symbols
2288}
2289
2290fn transition_expected_token_set(
2291    transition: ParserTransition<'_>,
2292    max_token_type: i32,
2293) -> TokenBitSet {
2294    let mut symbols = TokenBitSet::default();
2295    match &transition.data() {
2296        Transition::Atom { label, .. } => {
2297            symbols.insert(*label);
2298        }
2299        Transition::Range { start, stop, .. } => {
2300            symbols.extend_range(*start, *stop);
2301        }
2302        Transition::Set { set, .. } => {
2303            for (start, stop) in set.ranges() {
2304                symbols.extend_range(start, stop);
2305            }
2306        }
2307        Transition::NotSet { set, .. } => {
2308            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2309        }
2310        Transition::Wildcard { .. } => {
2311            symbols.extend_range(1, max_token_type);
2312        }
2313        Transition::Epsilon { .. }
2314        | Transition::Rule { .. }
2315        | Transition::Predicate { .. }
2316        | Transition::Action { .. }
2317        | Transition::Precedence { .. } => {}
2318    }
2319    symbols
2320}
2321
2322/// Returns the consuming-token expectations reachable from an ATN state through
2323/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2324/// and loop exits report the same expectation set ANTLR users see.
2325fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2326    let mut symbols = BTreeSet::new();
2327    let mut stack = vec![state_number];
2328    let mut visited = BTreeSet::new();
2329    while let Some(current) = stack.pop() {
2330        if !visited.insert(current) {
2331            continue;
2332        }
2333        let Some(state) = atn.state(current) else {
2334            continue;
2335        };
2336        for transition in &state.transitions() {
2337            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2338            if transition_symbols.is_empty() {
2339                if transition.is_epsilon() {
2340                    stack.push(transition.target());
2341                }
2342            } else {
2343                symbols.extend(transition_symbols);
2344            }
2345        }
2346    }
2347    symbols
2348}
2349
2350fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2351    let mut symbols = TokenBitSet::default();
2352    let mut stack = vec![state_number];
2353    let mut visited = BTreeSet::new();
2354    while let Some(current) = stack.pop() {
2355        if !visited.insert(current) {
2356            continue;
2357        }
2358        let Some(state) = atn.state(current) else {
2359            continue;
2360        };
2361        for transition in &state.transitions() {
2362            let transition_symbols =
2363                transition_expected_token_set(transition, atn.max_token_type());
2364            if transition_symbols.is_empty() {
2365                if transition.is_epsilon() {
2366                    stack.push(transition.target());
2367                }
2368            } else {
2369                symbols.extend_from(&transition_symbols);
2370            }
2371        }
2372    }
2373    symbols
2374}
2375
2376fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2377    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2378        return false;
2379    };
2380    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2381        return false;
2382    };
2383    epsilon_reaches_state(atn, state_number, stop_state)
2384}
2385
2386fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2387    let mut stack = vec![start];
2388    let mut visited = BTreeSet::new();
2389    while let Some(current) = stack.pop() {
2390        if current == target {
2391            return true;
2392        }
2393        if !visited.insert(current) {
2394            continue;
2395        }
2396        let Some(state) = atn.state(current) else {
2397            continue;
2398        };
2399        stack.extend(
2400            state
2401                .transitions()
2402                .iter()
2403                .filter(|transition| transition.is_epsilon())
2404                .map(ParserTransition::target),
2405        );
2406    }
2407    false
2408}
2409
2410/// FIRST set for a rule entry plus whether the rule is nullable.
2411///
2412/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2413/// a consuming transition or reaches the rule's stop state. Used by the fast
2414/// recognizer to skip rule alternatives whose first-consumed token cannot
2415/// possibly match the current lookahead.
2416#[derive(Clone, Debug, Default, Eq, PartialEq)]
2417struct FirstSet {
2418    symbols: TokenBitSet,
2419    nullable: bool,
2420}
2421
2422/// Per-parser cache of FIRST sets computed during recognition. The fast path
2423/// consults this on every speculative `Transition::Rule` encounter, so the
2424/// computation must amortize across all of those calls — the FIRST set is a
2425/// pure function of the ATN, not of the input position. Cached entries are
2426/// shared via `Rc` so the recognizer never deep-copies the underlying
2427/// `BTreeSet<i32>`.
2428type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2429
2430// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2431// and decision-lookahead entries are purely functions of the grammar's
2432// ATN, so caching across parses lets repeated parsing of the same grammar
2433// (the common case for a CLI tool or language server) avoid redoing the
2434// closure work. Generated parsers hand us a `&'static Atn` whose address
2435// is stable, which is what we hash on.
2436type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2437
2438#[derive(Debug, Default)]
2439struct LeftRecursiveOperatorLookahead {
2440    /// Operator alts whose token-prefix is fully matched by this one symbol
2441    /// (then only epsilons/actions remain before the recursive RHS call).
2442    /// Safe for one-token loop-enter fast path.
2443    single_token: TokenBitSet,
2444    /// Operator alts that start with this symbol but still require more tokens
2445    /// before the operand. Must not force enter from one-token lookahead when a
2446    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2447    /// has to weigh the exit alt as well.
2448    multi_token_prefix: TokenBitSet,
2449    predicate_dependent: TokenBitSet,
2450}
2451
2452#[derive(Default)]
2453struct SharedAtnCache {
2454    first_set: FirstSetCache,
2455    decision_lookahead: DecisionLookaheadCache,
2456    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2457    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2458    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2459    rule_stop_reach: FxHashMap<usize, bool>,
2460    observable_action_transitions: Option<bool>,
2461    predicate_transitions: Option<bool>,
2462}
2463
2464thread_local! {
2465    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2466        RefCell::new(FxHashMap::default());
2467}
2468
2469/// Compound key for `SHARED_ATN_CACHES`.
2470///
2471/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2472/// pointer identifies one grammar for the program's lifetime. For the
2473/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2474/// the secondary fields below catch the pointer collision: a new grammar
2475/// would need to match all of `(states ptr, states len, max_token_type)` to
2476/// be mistaken for the dropped one. That combination changing under us
2477/// without a rebuild is implausible enough to treat as a bug; bundling them
2478/// into the key is otherwise a few extra bytes per lookup.
2479#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2480struct SharedAtnCacheKey {
2481    atn: usize,
2482    states: usize,
2483    state_count: usize,
2484    max_token_type: i32,
2485}
2486
2487impl SharedAtnCacheKey {
2488    fn for_atn(atn: &Atn) -> Self {
2489        let (states, state_count) = atn.storage_identity();
2490        Self {
2491            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2492            states,
2493            state_count,
2494            max_token_type: atn.max_token_type(),
2495        }
2496    }
2497}
2498
2499fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2500    SHARED_ATN_CACHES.with(|cell| {
2501        let key = SharedAtnCacheKey::for_atn(atn);
2502        let mut map = cell.borrow_mut();
2503        let cache = map.entry(key).or_default();
2504        f(&mut cache.first_set)
2505    })
2506}
2507
2508fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2509    SHARED_ATN_CACHES.with(|cell| {
2510        let key = SharedAtnCacheKey::for_atn(atn);
2511        let mut map = cell.borrow_mut();
2512        let cache = map.entry(key).or_default();
2513        f(cache)
2514    })
2515}
2516
2517/// Per-decision-state cached look-1 sets for each outgoing transition.
2518///
2519/// At a multi-alternative state, the recognizer would otherwise speculatively
2520/// walk every alternative even when only one can possibly accept the current
2521/// lookahead. Caching the look-1 set per transition lets us prune the
2522/// non-viable transitions before recursing — the same SLL prediction trick
2523/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2524/// filter rather than a full DFA.
2525#[derive(Debug, Default)]
2526struct DecisionLookahead {
2527    transitions: Vec<TransitionLookSet>,
2528}
2529
2530/// Look-1 information for one outgoing transition.
2531///
2532/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2533/// can reach the rule stop without consuming a token (e.g. an empty alt).
2534/// Nullable transitions cannot be pruned: they may still be the right path
2535/// when the lookahead consumes nothing further inside the current rule.
2536#[derive(Clone, Debug, Default)]
2537struct TransitionLookSet {
2538    symbols: TokenBitSet,
2539    nullable: bool,
2540}
2541
2542/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2543/// rarely-touched fields keeps the recursive helpers below the function-arity
2544/// lint and lets every nested call thread the same cache and cycle guards.
2545struct FirstSetCtx<'a> {
2546    cache: &'a mut FirstSetCache,
2547    in_progress: BTreeSet<(usize, usize)>,
2548    hit_cycle: bool,
2549}
2550
2551/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2552/// shared cache and tolerating recursive nullable rule chains. Mutually
2553/// recursive rules cannot stack-overflow because callers in flight are tracked
2554/// in `ctx.in_progress`; revisits return without recursing, and the partial
2555/// result is cached only when no cycle was detected during its computation.
2556///
2557/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2558/// rule-call probes only pay a reference bump.
2559fn rule_first_set(
2560    atn: &Atn,
2561    target: usize,
2562    rule_stop_state: usize,
2563    cache: &mut FirstSetCache,
2564) -> Rc<FirstSet> {
2565    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2566        return Rc::clone(cached);
2567    }
2568    let mut ctx = FirstSetCtx {
2569        cache,
2570        in_progress: BTreeSet::new(),
2571        hit_cycle: false,
2572    };
2573    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2574}
2575
2576fn rule_first_set_cached(
2577    atn: &Atn,
2578    target: usize,
2579    rule_stop_state: usize,
2580    ctx: &mut FirstSetCtx<'_>,
2581) -> Rc<FirstSet> {
2582    let key = (target, rule_stop_state);
2583    if let Some(cached) = ctx.cache.get(&key) {
2584        return Rc::clone(cached);
2585    }
2586    if !ctx.in_progress.insert(key) {
2587        // Cycle: a caller above is already computing this entry. Return an
2588        // empty FIRST set; that caller's traversal supplies the contributions
2589        // from the rule's other alternatives.
2590        return Rc::new(FirstSet::default());
2591    }
2592    let saved_hit_cycle = ctx.hit_cycle;
2593    ctx.hit_cycle = false;
2594    let mut first = FirstSet::default();
2595    let mut visited = BTreeSet::new();
2596    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2597    ctx.in_progress.remove(&key);
2598    let entry = Rc::new(first);
2599    if !ctx.hit_cycle {
2600        ctx.cache.insert(key, Rc::clone(&entry));
2601    }
2602    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2603    entry
2604}
2605
2606/// Returns the look-1 set for traversing `transition` while still inside the
2607/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2608/// prunes transitions whose look-1 cannot accept the current lookahead.
2609fn transition_first_set(
2610    atn: &Atn,
2611    transition: ParserTransition<'_>,
2612    rule_stop_state: usize,
2613    cache: &mut FirstSetCache,
2614) -> TransitionLookSet {
2615    match &transition.data() {
2616        Transition::Atom { label, .. } => {
2617            let mut symbols = TokenBitSet::default();
2618            symbols.insert(*label);
2619            TransitionLookSet {
2620                symbols,
2621                nullable: false,
2622            }
2623        }
2624        Transition::Range { start, stop, .. } => {
2625            let mut symbols = TokenBitSet::default();
2626            symbols.extend_range(*start, *stop);
2627            TransitionLookSet {
2628                symbols,
2629                nullable: false,
2630            }
2631        }
2632        Transition::Set { set, .. } => {
2633            let mut symbols = TokenBitSet::default();
2634            for (start, stop) in set.ranges() {
2635                symbols.extend_range(start, stop);
2636            }
2637            TransitionLookSet {
2638                symbols,
2639                nullable: false,
2640            }
2641        }
2642        Transition::NotSet { set, .. } => {
2643            let max = atn.max_token_type();
2644            let mut symbols = TokenBitSet::default();
2645            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2646            TransitionLookSet {
2647                symbols,
2648                nullable: false,
2649            }
2650        }
2651        Transition::Wildcard { .. } => {
2652            let mut symbols = TokenBitSet::default();
2653            symbols.extend_range(1, atn.max_token_type());
2654            TransitionLookSet {
2655                symbols,
2656                nullable: false,
2657            }
2658        }
2659        Transition::Epsilon { target }
2660        | Transition::Action { target, .. }
2661        | Transition::Predicate { target, .. }
2662        | Transition::Precedence { target, .. } => {
2663            // Walk the closure starting at `target` until a consuming transition
2664            // is reached or the rule stop state is hit.
2665            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2666            TransitionLookSet {
2667                symbols: first.symbols.clone(),
2668                nullable: first.nullable,
2669            }
2670        }
2671        Transition::Rule {
2672            target,
2673            rule_index,
2674            follow_state,
2675            ..
2676        } => {
2677            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2678                return TransitionLookSet::default();
2679            };
2680            let child = rule_first_set(atn, *target, child_stop, cache);
2681            let mut symbols = child.symbols.clone();
2682            let nullable = if child.nullable {
2683                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2684                symbols.extend_from(&follow.symbols);
2685                follow.nullable
2686            } else {
2687                false
2688            };
2689            TransitionLookSet { symbols, nullable }
2690        }
2691    }
2692}
2693
2694/// Reports whether `transition` can be pruned at a multi-alt state because
2695/// its cached look-1 cannot accept the current lookahead.
2696///
2697/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2698/// Rule/Precedence) so consuming transitions still reach the
2699/// `matches`+recovery path that surfaces single-token deletion / insertion
2700/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2701/// transition is pruned, its FIRST set is folded into `expected` so failed
2702/// parses produce the same `mismatched input ... expecting ...` diagnostic
2703/// the no-prefilter baseline would emit.
2704/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2705/// one transition's FIRST set and no transition is nullable. Used as an
2706/// LL(1) commit point: when prediction is unambiguous from the lookahead
2707/// alone, the recursive recognizer can skip every other alt without paying
2708/// for the per-transition filter probe.
2709///
2710/// `None` signals the caller to fall back to per-transition lookahead
2711/// filtering. Returning `Some` for an alt whose transition cannot actually
2712/// match would prune the only viable parse path; this is why we require
2713/// strict disjointness *and* no nullable transitions in the decision.
2714fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2715    let mut chosen: Option<usize> = None;
2716    for (index, transition) in entry.transitions.iter().enumerate() {
2717        if transition.nullable {
2718            return None;
2719        }
2720        if transition.symbols.contains(symbol) {
2721            if chosen.is_some() {
2722                return None;
2723            }
2724            chosen = Some(index);
2725        }
2726    }
2727    chosen
2728}
2729
2730/// Returns the unique greedy alt index (0-based) selected by the current
2731/// lookahead.
2732///
2733/// The shortcut is intentionally conservative around nullable exits. If the
2734/// current symbol can start a consuming alternative and an empty alternative is
2735/// also present, one-token lookahead is not enough to know whether the symbol
2736/// belongs to the current construct or to its caller's follow set. `None`
2737/// signals the caller to fall back to adaptive prediction.
2738fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2739    let mut matching_non_nullable_alt = None;
2740    let mut nullable_alt = None;
2741    for (index, transition) in entry.transitions.iter().enumerate() {
2742        if transition.nullable {
2743            if nullable_alt.is_some() {
2744                return None;
2745            }
2746            nullable_alt = Some(index);
2747        }
2748        if transition.symbols.contains(symbol) {
2749            if transition.nullable {
2750                continue;
2751            }
2752            if matching_non_nullable_alt.is_some() {
2753                return None;
2754            }
2755            matching_non_nullable_alt = Some(index);
2756        }
2757    }
2758    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
2759        return None;
2760    }
2761    if non_greedy {
2762        nullable_alt.or(matching_non_nullable_alt)
2763    } else {
2764        matching_non_nullable_alt.or(nullable_alt)
2765    }
2766}
2767
2768fn should_skip_via_lookahead(
2769    transition_kind: ParserTransitionKind,
2770    transition_index: usize,
2771    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
2772    index: usize,
2773    record_expected: bool,
2774    expected: &mut ExpectedTokens,
2775) -> bool {
2776    let prune_non_consuming = matches!(
2777        transition_kind,
2778        ParserTransitionKind::Epsilon
2779            | ParserTransitionKind::Action
2780            | ParserTransitionKind::Predicate
2781            | ParserTransitionKind::Rule
2782            | ParserTransitionKind::Precedence
2783    );
2784    if !prune_non_consuming {
2785        return false;
2786    }
2787    let Some((symbol, entry)) = lookahead_filter else {
2788        return false;
2789    };
2790    let Some(set) = entry.transitions.get(transition_index) else {
2791        return false;
2792    };
2793    if set.symbols.contains(*symbol) || set.nullable {
2794        return false;
2795    }
2796    if record_expected && !set.symbols.is_empty() {
2797        record_pruned_transition_expected(set, index, expected);
2798    }
2799    true
2800}
2801
2802fn should_skip_rule_via_first_set(
2803    first: &FirstSet,
2804    symbol: i32,
2805    record_expected: bool,
2806    index: usize,
2807    expected: &mut ExpectedTokens,
2808) -> bool {
2809    if first.nullable || first.symbols.contains(symbol) {
2810        return false;
2811    }
2812    if record_expected && !first.symbols.is_empty() {
2813        record_token_bit_expected(&first.symbols, index, expected);
2814    }
2815    true
2816}
2817
2818fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
2819    match expected.index {
2820        Some(current) if index < current => {}
2821        Some(current) if index == current => {
2822            symbols.extend_btree_set(&mut expected.symbols);
2823        }
2824        _ => {
2825            expected.index = Some(index);
2826            expected.symbols = symbols.to_btree_set();
2827        }
2828    }
2829}
2830
2831/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
2832fn record_pruned_transition_expected(
2833    set: &TransitionLookSet,
2834    index: usize,
2835    expected: &mut ExpectedTokens,
2836) {
2837    match expected.index {
2838        Some(current) if index < current => {}
2839        Some(current) if index == current => {
2840            set.symbols.extend_btree_set(&mut expected.symbols);
2841        }
2842        _ => {
2843            expected.index = Some(index);
2844            expected.symbols = set.symbols.to_btree_set();
2845        }
2846    }
2847}
2848
2849fn rule_first_set_inner(
2850    atn: &Atn,
2851    state_number: usize,
2852    rule_stop_state: usize,
2853    ctx: &mut FirstSetCtx<'_>,
2854    visited: &mut BTreeSet<usize>,
2855    first: &mut FirstSet,
2856) {
2857    if !visited.insert(state_number) {
2858        return;
2859    }
2860    if state_number == rule_stop_state {
2861        first.nullable = true;
2862        return;
2863    }
2864    let Some(state) = atn.state(state_number) else {
2865        return;
2866    };
2867    for transition in &state.transitions() {
2868        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2869        if !transition_symbols.is_empty() {
2870            first.symbols.extend_iter(transition_symbols);
2871            continue;
2872        }
2873        match &transition.data() {
2874            Transition::Epsilon { target }
2875            | Transition::Action { target, .. }
2876            | Transition::Predicate { target, .. }
2877            | Transition::Precedence { target, .. } => {
2878                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
2879            }
2880            Transition::Rule {
2881                target,
2882                rule_index,
2883                follow_state,
2884                ..
2885            } => {
2886                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2887                    continue;
2888                };
2889                let child_key = (*target, child_stop);
2890                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
2891                    ctx.hit_cycle = true;
2892                }
2893                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
2894                first.symbols.extend_from(&child.symbols);
2895                if child.nullable {
2896                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
2897                }
2898            }
2899            Transition::Atom { .. }
2900            | Transition::Range { .. }
2901            | Transition::Set { .. }
2902            | Transition::NotSet { .. }
2903            | Transition::Wildcard { .. } => {}
2904        }
2905    }
2906}
2907
2908/// Returns token types that can resume parsing from `state_number` after a
2909/// failed child rule, following rule calls as well as epsilon transitions.
2910fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
2911    let mut symbols = BTreeSet::new();
2912    state_sync_symbols_inner(
2913        atn,
2914        state_number,
2915        stop_state,
2916        &mut BTreeSet::new(),
2917        &mut symbols,
2918    );
2919    symbols
2920}
2921
2922/// Walks epsilon-like continuations from a parent follow state until it finds
2923/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
2924fn state_sync_symbols_inner(
2925    atn: &Atn,
2926    state_number: usize,
2927    stop_state: usize,
2928    visited: &mut BTreeSet<usize>,
2929    symbols: &mut BTreeSet<i32>,
2930) {
2931    if !visited.insert(state_number) {
2932        return;
2933    }
2934    if state_number == stop_state {
2935        symbols.insert(TOKEN_EOF);
2936        return;
2937    }
2938    let Some(state) = atn.state(state_number) else {
2939        return;
2940    };
2941    for transition in &state.transitions() {
2942        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2943        if transition_symbols.is_empty() {
2944            match &transition.data() {
2945                Transition::Rule { target, .. }
2946                | Transition::Epsilon { target }
2947                | Transition::Action { target, .. }
2948                | Transition::Predicate { target, .. }
2949                | Transition::Precedence { target, .. } => {
2950                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
2951                }
2952                Transition::Atom { .. }
2953                | Transition::Range { .. }
2954                | Transition::Set { .. }
2955                | Transition::NotSet { .. }
2956                | Transition::Wildcard { .. } => {}
2957            }
2958        } else {
2959            symbols.extend(transition_symbols);
2960        }
2961    }
2962}
2963
2964#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2965struct OperatorSymbolReachability {
2966    /// One token completes an unconditional operator token-prefix.
2967    single_token: bool,
2968    /// An unconditional operator path requires more tokens before its operand.
2969    multi_token: bool,
2970    /// At least one matching operator path depends on a semantic predicate.
2971    predicate_dependent: bool,
2972}
2973
2974impl OperatorSymbolReachability {
2975    const ADAPTIVE_FALLBACK: Self = Self {
2976        single_token: false,
2977        multi_token: false,
2978        predicate_dependent: true,
2979    };
2980
2981    const fn single_token(predicate_dependent: bool) -> Self {
2982        if predicate_dependent {
2983            Self {
2984                single_token: false,
2985                multi_token: false,
2986                predicate_dependent: true,
2987            }
2988        } else {
2989            Self {
2990                single_token: true,
2991                multi_token: false,
2992                predicate_dependent: false,
2993            }
2994        }
2995    }
2996
2997    const fn multi_token(predicate_dependent: bool) -> Self {
2998        if predicate_dependent {
2999            Self {
3000                single_token: false,
3001                multi_token: false,
3002                predicate_dependent: true,
3003            }
3004        } else {
3005            Self {
3006                single_token: false,
3007                multi_token: true,
3008                predicate_dependent: false,
3009            }
3010        }
3011    }
3012
3013    const fn union(self, other: Self) -> Self {
3014        Self {
3015            single_token: self.single_token || other.single_token,
3016            multi_token: self.multi_token || other.multi_token,
3017            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3018        }
3019    }
3020}
3021
3022#[derive(Clone, Copy)]
3023struct OperatorReachabilityRequest {
3024    symbol: i32,
3025    precedence: i32,
3026    predicate_dependent: bool,
3027    operator_rule_index: usize,
3028}
3029
3030#[derive(Clone, Copy, Debug)]
3031struct OperatorRuleContinuation {
3032    stop_state: usize,
3033    follow_state: usize,
3034    return_precedence: i32,
3035}
3036
3037struct NullablePrecedenceCtx {
3038    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3039    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3040    hit_cycle: bool,
3041}
3042
3043fn state_is_nullable_with_precedence(
3044    atn: &Atn,
3045    state_number: usize,
3046    stop_state_number: usize,
3047    precedence: i32,
3048    allow_predicates: bool,
3049    ctx: &mut NullablePrecedenceCtx,
3050) -> bool {
3051    let saved_hit_cycle = ctx.hit_cycle;
3052    ctx.hit_cycle = false;
3053    let nullable = state_is_nullable_with_precedence_cached(
3054        atn,
3055        state_number,
3056        stop_state_number,
3057        precedence,
3058        allow_predicates,
3059        ctx,
3060    );
3061    ctx.hit_cycle = saved_hit_cycle;
3062    nullable
3063}
3064
3065fn state_is_nullable_with_precedence_cached(
3066    atn: &Atn,
3067    state_number: usize,
3068    stop_state_number: usize,
3069    precedence: i32,
3070    allow_predicates: bool,
3071    ctx: &mut NullablePrecedenceCtx,
3072) -> bool {
3073    if state_number == stop_state_number {
3074        return true;
3075    }
3076    let key = (
3077        state_number,
3078        stop_state_number,
3079        precedence,
3080        allow_predicates,
3081    );
3082    if let Some(cached) = ctx.cache.get(&key) {
3083        return *cached;
3084    }
3085    if !ctx.in_progress.insert(key) {
3086        ctx.hit_cycle = true;
3087        return false;
3088    }
3089    let saved_hit_cycle = ctx.hit_cycle;
3090    ctx.hit_cycle = false;
3091    let nullable = atn.state(state_number).is_some_and(|state| {
3092        state
3093            .transitions()
3094            .iter()
3095            .any(|transition| match &transition.data() {
3096                Transition::Rule {
3097                    target,
3098                    rule_index,
3099                    follow_state,
3100                    precedence: rule_precedence,
3101                } => {
3102                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3103                        return false;
3104                    };
3105                    state_is_nullable_with_precedence_cached(
3106                        atn,
3107                        *target,
3108                        child_stop,
3109                        *rule_precedence,
3110                        allow_predicates,
3111                        ctx,
3112                    ) && state_is_nullable_with_precedence_cached(
3113                        atn,
3114                        *follow_state,
3115                        stop_state_number,
3116                        precedence,
3117                        allow_predicates,
3118                        ctx,
3119                    )
3120                }
3121                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3122                    state_is_nullable_with_precedence_cached(
3123                        atn,
3124                        *target,
3125                        stop_state_number,
3126                        precedence,
3127                        allow_predicates,
3128                        ctx,
3129                    )
3130                }
3131                Transition::Predicate { target, .. } if allow_predicates => {
3132                    state_is_nullable_with_precedence_cached(
3133                        atn,
3134                        *target,
3135                        stop_state_number,
3136                        precedence,
3137                        allow_predicates,
3138                        ctx,
3139                    )
3140                }
3141                Transition::Precedence {
3142                    target,
3143                    precedence: transition_precedence,
3144                } if *transition_precedence >= precedence => {
3145                    state_is_nullable_with_precedence_cached(
3146                        atn,
3147                        *target,
3148                        stop_state_number,
3149                        precedence,
3150                        allow_predicates,
3151                        ctx,
3152                    )
3153                }
3154                Transition::Atom { .. }
3155                | Transition::Range { .. }
3156                | Transition::Set { .. }
3157                | Transition::NotSet { .. }
3158                | Transition::Wildcard { .. }
3159                | Transition::Predicate { .. }
3160                | Transition::Precedence { .. } => false,
3161            })
3162    });
3163    ctx.in_progress.remove(&key);
3164    if !ctx.hit_cycle {
3165        ctx.cache.insert(key, nullable);
3166    }
3167    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3168    nullable
3169}
3170
3171/// Classifies what remains after the operator's first token is matched.
3172fn state_operator_token_prefix_reachability(
3173    atn: &Atn,
3174    state_number: usize,
3175    request: OperatorReachabilityRequest,
3176    continuations: &[OperatorRuleContinuation],
3177    visited: &mut BTreeSet<(usize, i32, bool)>,
3178) -> OperatorSymbolReachability {
3179    let key = (
3180        state_number,
3181        request.precedence,
3182        request.predicate_dependent,
3183    );
3184    if !visited.insert(key) {
3185        // Recursive helper rules can grow the return stack without consuming
3186        // input. Delegate cycles to adaptive prediction instead of forcing a
3187        // potentially incomplete one-token answer.
3188        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3189    }
3190    if let Some((continuation, remaining)) = continuations.split_last()
3191        && state_number == continuation.stop_state
3192    {
3193        let result = state_operator_token_prefix_reachability(
3194            atn,
3195            continuation.follow_state,
3196            OperatorReachabilityRequest {
3197                precedence: continuation.return_precedence,
3198                ..request
3199            },
3200            remaining,
3201            visited,
3202        );
3203        visited.remove(&key);
3204        return result;
3205    }
3206    let Some(state) = atn.state(state_number) else {
3207        visited.remove(&key);
3208        return OperatorSymbolReachability::default();
3209    };
3210    let completes_operator = match state.kind() {
3211        AtnStateKind::RuleStop => continuations.is_empty(),
3212        AtnStateKind::StarLoopBack
3213        | AtnStateKind::StarLoopEntry
3214        | AtnStateKind::PlusLoopBack
3215        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3216        _ => false,
3217    };
3218    if completes_operator {
3219        visited.remove(&key);
3220        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3221    }
3222    let mut reachability = OperatorSymbolReachability::default();
3223    for transition in &state.transitions() {
3224        let transition_reachability = match &transition.data() {
3225            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3226                OperatorSymbolReachability::single_token(request.predicate_dependent)
3227            }
3228            Transition::Rule {
3229                target,
3230                rule_index,
3231                follow_state,
3232                precedence: rule_precedence,
3233            } => {
3234                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3235                    continue;
3236                };
3237                let mut nested = continuations.to_vec();
3238                nested.push(OperatorRuleContinuation {
3239                    stop_state: child_stop,
3240                    follow_state: *follow_state,
3241                    return_precedence: request.precedence,
3242                });
3243                state_operator_token_prefix_reachability(
3244                    atn,
3245                    *target,
3246                    OperatorReachabilityRequest {
3247                        precedence: *rule_precedence,
3248                        ..request
3249                    },
3250                    &nested,
3251                    visited,
3252                )
3253            }
3254            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3255                state_operator_token_prefix_reachability(
3256                    atn,
3257                    *target,
3258                    request,
3259                    continuations,
3260                    visited,
3261                )
3262            }
3263            Transition::Precedence {
3264                target,
3265                precedence: transition_precedence,
3266            } => {
3267                if *transition_precedence < request.precedence {
3268                    OperatorSymbolReachability::default()
3269                } else {
3270                    state_operator_token_prefix_reachability(
3271                        atn,
3272                        *target,
3273                        request,
3274                        continuations,
3275                        visited,
3276                    )
3277                }
3278            }
3279            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3280                atn,
3281                *target,
3282                OperatorReachabilityRequest {
3283                    predicate_dependent: true,
3284                    ..request
3285                },
3286                continuations,
3287                visited,
3288            ),
3289            Transition::Atom { .. }
3290            | Transition::Range { .. }
3291            | Transition::Set { .. }
3292            | Transition::NotSet { .. }
3293            | Transition::Wildcard { .. } => {
3294                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3295            }
3296        };
3297        reachability = reachability.union(transition_reachability);
3298    }
3299    visited.remove(&key);
3300    reachability
3301}
3302
3303fn state_can_reach_symbol_with_precedence(
3304    atn: &Atn,
3305    state_number: usize,
3306    request: OperatorReachabilityRequest,
3307    nullable_ctx: &mut NullablePrecedenceCtx,
3308    continuations: &mut Vec<OperatorRuleContinuation>,
3309    visited: &mut BTreeSet<(usize, i32, bool)>,
3310) -> OperatorSymbolReachability {
3311    let key = (
3312        state_number,
3313        request.precedence,
3314        request.predicate_dependent,
3315    );
3316    if !visited.insert(key) {
3317        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3318    }
3319    let Some(state) = atn.state(state_number) else {
3320        visited.remove(&key);
3321        return OperatorSymbolReachability::default();
3322    };
3323    let mut reachability = OperatorSymbolReachability::default();
3324    for transition in &state.transitions() {
3325        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3326            reachability = reachability.union(state_operator_token_prefix_reachability(
3327                atn,
3328                transition.target(),
3329                request,
3330                continuations,
3331                &mut BTreeSet::new(),
3332            ));
3333            continue;
3334        }
3335        let transition_reachability = match &transition.data() {
3336            Transition::Rule {
3337                target,
3338                rule_index,
3339                follow_state,
3340                precedence: rule_precedence,
3341            } => {
3342                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3343                    continue;
3344                };
3345                continuations.push(OperatorRuleContinuation {
3346                    stop_state: child_stop,
3347                    follow_state: *follow_state,
3348                    return_precedence: request.precedence,
3349                });
3350                let mut result = state_can_reach_symbol_with_precedence(
3351                    atn,
3352                    *target,
3353                    OperatorReachabilityRequest {
3354                        precedence: *rule_precedence,
3355                        ..request
3356                    },
3357                    nullable_ctx,
3358                    continuations,
3359                    visited,
3360                );
3361                continuations.pop();
3362                if state_is_nullable_with_precedence(
3363                    atn,
3364                    *target,
3365                    child_stop,
3366                    *rule_precedence,
3367                    true,
3368                    nullable_ctx,
3369                ) {
3370                    let child_predicate_dependent = request.predicate_dependent
3371                        || !state_is_nullable_with_precedence(
3372                            atn,
3373                            *target,
3374                            child_stop,
3375                            *rule_precedence,
3376                            false,
3377                            nullable_ctx,
3378                        );
3379                    result = result.union(state_can_reach_symbol_with_precedence(
3380                        atn,
3381                        *follow_state,
3382                        OperatorReachabilityRequest {
3383                            predicate_dependent: child_predicate_dependent,
3384                            ..request
3385                        },
3386                        nullable_ctx,
3387                        continuations,
3388                        visited,
3389                    ));
3390                }
3391                result
3392            }
3393            Transition::Epsilon { target }
3394            | Transition::Action { target, .. }
3395            | Transition::Precedence { target, .. } => {
3396                if matches!(
3397                    &transition.data(),
3398                    Transition::Precedence {
3399                        precedence: transition_precedence,
3400                        ..
3401                    } if *transition_precedence < request.precedence
3402                ) {
3403                    continue;
3404                }
3405                state_can_reach_symbol_with_precedence(
3406                    atn,
3407                    *target,
3408                    request,
3409                    nullable_ctx,
3410                    continuations,
3411                    visited,
3412                )
3413            }
3414            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3415                atn,
3416                *target,
3417                OperatorReachabilityRequest {
3418                    predicate_dependent: true,
3419                    ..request
3420                },
3421                nullable_ctx,
3422                continuations,
3423                visited,
3424            ),
3425            Transition::Atom { .. }
3426            | Transition::Range { .. }
3427            | Transition::Set { .. }
3428            | Transition::NotSet { .. }
3429            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3430        };
3431        reachability = reachability.union(transition_reachability);
3432    }
3433    visited.remove(&key);
3434    reachability
3435}
3436
3437fn left_recursive_operator_lookahead(
3438    atn: &Atn,
3439    state_number: usize,
3440    precedence: i32,
3441) -> LeftRecursiveOperatorLookahead {
3442    let Some(state) = atn.state(state_number) else {
3443        return LeftRecursiveOperatorLookahead::default();
3444    };
3445    let Some(operator_rule_index) = state.rule_index() else {
3446        return LeftRecursiveOperatorLookahead::default();
3447    };
3448    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3449    let mut nullable_ctx = NullablePrecedenceCtx {
3450        cache: FxHashMap::default(),
3451        in_progress: BTreeSet::new(),
3452        hit_cycle: false,
3453    };
3454    for transition in &state.transitions() {
3455        let target = transition.target();
3456        if atn
3457            .state(target)
3458            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3459        {
3460            continue;
3461        }
3462        for symbol in 1..=atn.max_token_type() {
3463            let reachability = state_can_reach_symbol_with_precedence(
3464                atn,
3465                target,
3466                OperatorReachabilityRequest {
3467                    symbol,
3468                    precedence,
3469                    predicate_dependent: false,
3470                    operator_rule_index,
3471                },
3472                &mut nullable_ctx,
3473                &mut Vec::new(),
3474                &mut BTreeSet::new(),
3475            );
3476            if reachability.single_token {
3477                lookahead.single_token.insert(symbol);
3478            }
3479            if reachability.multi_token {
3480                lookahead.multi_token_prefix.insert(symbol);
3481            }
3482            if reachability.predicate_dependent {
3483                lookahead.predicate_dependent.insert(symbol);
3484            }
3485        }
3486    }
3487    lookahead
3488}
3489
3490#[derive(Debug, Default)]
3491struct StateBeforeStopLookahead {
3492    symbols: TokenBitSet,
3493    reaches_context_boundary: bool,
3494}
3495
3496fn state_before_stop_lookahead(
3497    atn: &Atn,
3498    state_number: usize,
3499    stop_state_number: usize,
3500) -> Rc<StateBeforeStopLookahead> {
3501    with_shared_atn_caches(atn, |cache| {
3502        let key = (state_number, stop_state_number);
3503        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3504            return Rc::clone(cached);
3505        }
3506        let mut lookahead = StateBeforeStopLookahead::default();
3507        state_before_stop_lookahead_inner(
3508            atn,
3509            state_number,
3510            stop_state_number,
3511            &mut BTreeSet::new(),
3512            &mut cache.first_set,
3513            &mut lookahead,
3514        );
3515        let lookahead = Rc::new(lookahead);
3516        cache
3517            .state_before_stop_lookahead
3518            .insert(key, Rc::clone(&lookahead));
3519        lookahead
3520    })
3521}
3522
3523fn state_before_stop_lookahead_inner(
3524    atn: &Atn,
3525    state_number: usize,
3526    stop_state_number: usize,
3527    visited: &mut BTreeSet<usize>,
3528    first_set_cache: &mut FirstSetCache,
3529    lookahead: &mut StateBeforeStopLookahead,
3530) {
3531    if state_number == stop_state_number {
3532        lookahead.reaches_context_boundary = true;
3533        return;
3534    }
3535    if !visited.insert(state_number) {
3536        return;
3537    }
3538    let Some(state) = atn.state(state_number) else {
3539        return;
3540    };
3541    if state.kind() == AtnStateKind::RuleStop {
3542        lookahead.reaches_context_boundary = true;
3543        return;
3544    }
3545    for transition in &state.transitions() {
3546        match &transition.data() {
3547            Transition::Epsilon { target }
3548            | Transition::Action { target, .. }
3549            | Transition::Predicate { target, .. }
3550            | Transition::Precedence { target, .. } => {
3551                state_before_stop_lookahead_inner(
3552                    atn,
3553                    *target,
3554                    stop_state_number,
3555                    visited,
3556                    first_set_cache,
3557                    lookahead,
3558                );
3559            }
3560            Transition::Rule {
3561                target,
3562                rule_index,
3563                follow_state,
3564                ..
3565            } => {
3566                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3567                    continue;
3568                };
3569                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3570                lookahead.symbols.extend_from(&child.symbols);
3571                if child.nullable {
3572                    state_before_stop_lookahead_inner(
3573                        atn,
3574                        *follow_state,
3575                        stop_state_number,
3576                        visited,
3577                        first_set_cache,
3578                        lookahead,
3579                    );
3580                }
3581            }
3582            Transition::Atom { .. }
3583            | Transition::Range { .. }
3584            | Transition::Set { .. }
3585            | Transition::NotSet { .. }
3586            | Transition::Wildcard { .. } => {
3587                lookahead.symbols.extend_iter(transition_expected_symbols(
3588                    transition,
3589                    atn.max_token_type(),
3590                ));
3591            }
3592        }
3593    }
3594}
3595
3596fn caller_context_can_match_symbol_before_state(
3597    atn: &Atn,
3598    return_states: impl DoubleEndedIterator<Item = usize>,
3599    stop_state_number: usize,
3600    symbol: i32,
3601) -> bool {
3602    for return_state in return_states.rev() {
3603        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3604        if lookahead.symbols.contains(symbol) {
3605            return true;
3606        }
3607        if !lookahead.reaches_context_boundary {
3608            return false;
3609        }
3610    }
3611    false
3612}
3613
3614/// Carries recovery expectations and their restart state through epsilon-only
3615/// paths. ANTLR can report and repair at the decision state even when the
3616/// failed consuming transition is nested under block or loop epsilon edges.
3617fn next_recovery_context(
3618    atn: &Atn,
3619    state: AtnState<'_>,
3620    inherited: &BTreeSet<i32>,
3621    inherited_state: Option<usize>,
3622) -> (BTreeSet<i32>, Option<usize>) {
3623    let state_symbols = state_expected_symbols(atn, state.state_number());
3624    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3625        let mut symbols = state_symbols;
3626        symbols.extend(inherited.iter().copied());
3627        return (symbols, Some(state.state_number()));
3628    }
3629    (inherited.clone(), inherited_state)
3630}
3631
3632fn recovery_expected_symbols(
3633    atn: &Atn,
3634    state_number: usize,
3635    inherited: &BTreeSet<i32>,
3636) -> BTreeSet<i32> {
3637    let mut symbols = state_expected_symbols(atn, state_number);
3638    symbols.extend(inherited.iter().copied());
3639    symbols
3640}
3641
3642/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3643/// parser's cached state-expected-symbols sets and the inherited `Rc`
3644/// without copying when the state cannot widen recovery.
3645fn fast_next_recovery_context<S, H>(
3646    parser: &mut BaseParser<S, H>,
3647    atn: &Atn,
3648    state: AtnState<'_>,
3649    inherited: &Rc<BTreeSet<i32>>,
3650    inherited_state: Option<usize>,
3651) -> (Rc<BTreeSet<i32>>, Option<usize>)
3652where
3653    S: TokenSource,
3654    H: SemanticHooks,
3655{
3656    if state.transitions().len() <= 1 {
3657        return (Rc::clone(inherited), inherited_state);
3658    }
3659    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3660    if state_symbols.is_empty() {
3661        return (Rc::clone(inherited), inherited_state);
3662    }
3663    if inherited.is_empty() {
3664        return (state_symbols, Some(state.state_number()));
3665    }
3666    if Rc::ptr_eq(&state_symbols, inherited) {
3667        return (state_symbols, Some(state.state_number()));
3668    }
3669    let mut combined = (*state_symbols).clone();
3670    combined.extend(inherited.iter().copied());
3671    (
3672        parser.intern_recovery_symbols(combined),
3673        Some(state.state_number()),
3674    )
3675}
3676
3677/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3678/// cached state-expected-symbols and avoids cloning when no widening is
3679/// needed.
3680fn fast_recovery_expected_symbols<S, H>(
3681    parser: &mut BaseParser<S, H>,
3682    atn: &Atn,
3683    state_number: usize,
3684    inherited: &Rc<BTreeSet<i32>>,
3685) -> Rc<BTreeSet<i32>>
3686where
3687    S: TokenSource,
3688    H: SemanticHooks,
3689{
3690    let cached = parser.cached_state_expected_symbols(atn, state_number);
3691    if inherited.is_empty() {
3692        return cached;
3693    }
3694    if cached.is_empty() {
3695        return Rc::clone(inherited);
3696    }
3697    if Rc::ptr_eq(&cached, inherited) {
3698        return cached;
3699    }
3700    let mut combined = (*cached).clone();
3701    combined.extend(inherited.iter().copied());
3702    parser.intern_recovery_symbols(combined)
3703}
3704
3705struct ParserTableSemCtx<'a> {
3706    member_values: &'a mut BTreeMap<usize, i64>,
3707    return_values: &'a mut BTreeMap<String, i64>,
3708}
3709
3710impl semir::PredContext for ParserTableSemCtx<'_> {
3711    type TokenText<'a>
3712        = &'a str
3713    where
3714        Self: 'a;
3715
3716    fn la(&mut self, _offset: isize) -> i64 {
3717        i64::from(TOKEN_EOF)
3718    }
3719
3720    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3721        None
3722    }
3723
3724    fn token_index_adjacent(&mut self) -> bool {
3725        false
3726    }
3727
3728    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3729        None
3730    }
3731
3732    fn member(&self, member: usize) -> Option<i64> {
3733        Some(self.member_values.get(&member).copied().unwrap_or_default())
3734    }
3735
3736    fn local_arg(&self) -> Option<i64> {
3737        None
3738    }
3739
3740    fn column(&self) -> Option<i64> {
3741        None
3742    }
3743
3744    fn token_start_column(&self) -> Option<i64> {
3745        None
3746    }
3747
3748    fn token_text_so_far(&self) -> Option<String> {
3749        None
3750    }
3751
3752    fn hook(&mut self, _hook: HookId) -> bool {
3753        false
3754    }
3755}
3756
3757impl semir::ActContext for ParserTableSemCtx<'_> {
3758    fn set_member(&mut self, member: usize, value: i64) {
3759        self.member_values.insert(member, value);
3760    }
3761
3762    fn set_return(&mut self, name: &str, value: i64) {
3763        self.return_values.insert(name.to_owned(), value);
3764    }
3765
3766    fn action_hook(&mut self, _hook: HookId) {}
3767}
3768
3769/// Applies generated integer-member side effects to one speculative path.
3770fn apply_member_actions(
3771    source_state: usize,
3772    actions: &[ParserMemberAction],
3773    semantics: Option<&ParserSemantics>,
3774    values: &mut BTreeMap<usize, i64>,
3775) {
3776    for action in actions
3777        .iter()
3778        .filter(|action| action.source_state == source_state)
3779    {
3780        *values.entry(action.member).or_default() += action.delta;
3781    }
3782    let Some(semantics) = semantics else {
3783        return;
3784    };
3785    let mut return_values = BTreeMap::new();
3786    let mut ctx = ParserTableSemCtx {
3787        member_values: values,
3788        return_values: &mut return_values,
3789    };
3790    for action in semantics
3791        .actions
3792        .iter()
3793        .filter(|action| action.source_state == source_state && action.speculative)
3794    {
3795        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3796    }
3797}
3798
3799/// Returns the speculative member state after replaying one ATN action state.
3800fn member_values_after_action(
3801    source_state: usize,
3802    actions: &[ParserMemberAction],
3803    semantics: Option<&ParserSemantics>,
3804    values: &BTreeMap<usize, i64>,
3805) -> BTreeMap<usize, i64> {
3806    let mut values = values.clone();
3807    apply_member_actions(source_state, actions, semantics, &mut values);
3808    values
3809}
3810
3811/// Returns the speculative rule-return state after replaying one ATN action.
3812fn return_values_after_action(
3813    source_state: usize,
3814    rule_index: usize,
3815    actions: &[ParserReturnAction],
3816    semantics: Option<&ParserSemantics>,
3817    values: &BTreeMap<String, i64>,
3818) -> BTreeMap<String, i64> {
3819    let mut values = values.clone();
3820    for action in actions
3821        .iter()
3822        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
3823    {
3824        values.insert(action.name.to_owned(), action.value);
3825    }
3826    if let Some(semantics) = semantics {
3827        let mut member_values = BTreeMap::new();
3828        let mut ctx = ParserTableSemCtx {
3829            member_values: &mut member_values,
3830            return_values: &mut values,
3831        };
3832        for action in semantics.actions.iter().filter(|action| {
3833            action.source_state == source_state
3834                && action.rule_index == rule_index
3835                && !action.speculative
3836        }) {
3837            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3838        }
3839    }
3840    values
3841}
3842
3843/// Resolves the integer argument visible to a child rule invocation.
3844fn rule_local_int_arg(
3845    rule_args: &[ParserRuleArg],
3846    source_state: usize,
3847    rule_index: usize,
3848    local_int_arg: Option<(usize, i64)>,
3849) -> Option<(usize, i64)> {
3850    rule_args
3851        .iter()
3852        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
3853        .map(|arg| {
3854            let value = if arg.inherit_local {
3855                local_int_arg.map_or(arg.value, |(_, value)| value)
3856            } else {
3857                arg.value
3858            };
3859            (rule_index, value)
3860        })
3861}
3862
3863/// Builds the terminal recognition outcome for a path that reached its stop
3864/// state.
3865fn stop_outcome(
3866    index: usize,
3867    consumed_eof: bool,
3868    rule_alt_number: usize,
3869    member_values: BTreeMap<usize, i64>,
3870    return_values: BTreeMap<String, i64>,
3871) -> Vec<RecognizeOutcome> {
3872    vec![RecognizeOutcome {
3873        index,
3874        consumed_eof,
3875        alt_number: rule_alt_number,
3876        member_values,
3877        return_values,
3878        diagnostics: DiagnosticSeqId::EMPTY,
3879        decisions: Vec::new(),
3880        actions: Vec::new(),
3881        nodes: NodeSeqId::EMPTY,
3882    }]
3883}
3884
3885fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
3886    with_shared_atn_caches(atn, |cache| {
3887        *cache.observable_action_transitions.get_or_insert_with(|| {
3888            atn.states().any(|state| {
3889                state.transitions().iter().any(|transition| {
3890                    matches!(
3891                        &transition.data(),
3892                        Transition::Action {
3893                            action_index: Some(_),
3894                            ..
3895                        }
3896                    )
3897                })
3898            })
3899        })
3900    })
3901}
3902
3903fn atn_has_predicate_transitions(atn: &Atn) -> bool {
3904    with_shared_atn_caches(atn, |cache| {
3905        *cache.predicate_transitions.get_or_insert_with(|| {
3906            atn.states().any(|state| {
3907                state
3908                    .transitions()
3909                    .iter()
3910                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
3911            })
3912        })
3913    })
3914}
3915
3916/// Reports whether predicates are the only observable semantics the fast
3917/// recognizer must preserve. Without path-local actions, arguments, or return
3918/// state, repeated evaluation at one coordinate and input index receives the
3919/// same runtime context.
3920fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
3921    options.init_action_rules.is_empty()
3922        && !options.track_alt_numbers
3923        && options
3924            .predicates
3925            .iter()
3926            .all(|(_, _, predicate)| predicate.failure_message().is_none())
3927        && options.semantics.is_none_or(|semantics| {
3928            semantics.actions.is_empty()
3929                && semantics
3930                    .predicates
3931                    .iter()
3932                    .all(|predicate| predicate.failure_message.is_none())
3933        })
3934        && options.rule_args.is_empty()
3935        && options.member_actions.is_empty()
3936        && options.return_actions.is_empty()
3937        && !atn_has_observable_action_transitions(atn)
3938}
3939
3940#[derive(Clone, Debug, Eq, PartialEq)]
3941struct RecognizeRequest<'a> {
3942    state_number: usize,
3943    stop_state: usize,
3944    index: usize,
3945    rule_start_index: usize,
3946    decision_start_index: Option<usize>,
3947    init_action_rules: &'a BTreeSet<usize>,
3948    predicates: &'a [(usize, usize, ParserPredicate)],
3949    semantics: Option<&'a ParserSemantics>,
3950    rule_args: &'a [ParserRuleArg],
3951    member_actions: &'a [ParserMemberAction],
3952    return_actions: &'a [ParserReturnAction],
3953    local_int_arg: Option<(usize, i64)>,
3954    member_values: BTreeMap<usize, i64>,
3955    return_values: BTreeMap<String, i64>,
3956    rule_alt_number: usize,
3957    track_alt_numbers: bool,
3958    consumed_eof: bool,
3959    /// Current left-recursive precedence threshold, matching ANTLR's
3960    /// `precpred(_ctx, k)` check for generated precedence rules.
3961    precedence: i32,
3962    depth: usize,
3963    recovery_symbols: BTreeSet<i32>,
3964    recovery_state: Option<usize>,
3965}
3966
3967#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
3968struct RecognizeKey {
3969    state_number: usize,
3970    stop_state: usize,
3971    index: usize,
3972    rule_start_index: usize,
3973    decision_start_index: Option<usize>,
3974    local_int_arg: Option<(usize, i64)>,
3975    member_values: BTreeMap<usize, i64>,
3976    return_values: BTreeMap<String, i64>,
3977    rule_alt_number: usize,
3978    track_alt_numbers: bool,
3979    consumed_eof: bool,
3980    precedence: i32,
3981    recovery_symbols: BTreeSet<i32>,
3982    recovery_state: Option<usize>,
3983}
3984
3985#[derive(Clone, Debug, Eq, PartialEq)]
3986struct EpsilonActionStep {
3987    source_state: usize,
3988    target: usize,
3989    action_rule_index: Option<usize>,
3990    left_recursive_boundary: Option<usize>,
3991    decision: Option<usize>,
3992    decision_start_index: Option<usize>,
3993    alt_number: usize,
3994    recovery_symbols: BTreeSet<i32>,
3995    recovery_state: Option<usize>,
3996}
3997
3998struct RecognizeScratch<'a> {
3999    visiting: &'a mut BTreeSet<RecognizeKey>,
4000    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4001    expected: &'a mut ExpectedTokens,
4002}
4003
4004#[derive(Clone, Debug, Eq, PartialEq)]
4005struct FastRecognizeRequest {
4006    state_number: usize,
4007    stop_state: usize,
4008    index: usize,
4009    rule_start_index: usize,
4010    decision_start_index: Option<usize>,
4011    precedence: i32,
4012    depth: usize,
4013    recovery_symbols: Rc<BTreeSet<i32>>,
4014    recovery_state: Option<usize>,
4015}
4016
4017#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4018struct FastRecognizeTopRequest {
4019    start_state: usize,
4020    stop_state: usize,
4021    start_index: usize,
4022    precedence: i32,
4023    caller_follow_state: Option<usize>,
4024}
4025
4026#[derive(Clone, Copy, Debug)]
4027struct FastPredicateContext<'a> {
4028    predicates: &'a [(usize, usize, ParserPredicate)],
4029    semantics: Option<&'a ParserSemantics>,
4030    member_values: &'a BTreeMap<usize, i64>,
4031}
4032
4033struct FastRecognizeScratch<'a, 'b> {
4034    predicate_context: Option<FastPredicateContext<'a>>,
4035    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4036    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4037    expected: &'b mut ExpectedTokens,
4038    native_depth: usize,
4039}
4040
4041#[derive(Clone, Copy, Debug)]
4042struct FastRepetitionShape {
4043    enter_target: usize,
4044    exit_target: usize,
4045    body_stop_state: usize,
4046    enter_transition_index: usize,
4047    exit_transition_index: usize,
4048}
4049
4050#[derive(Clone, Copy, Debug)]
4051struct FastRepetitionPath {
4052    index: usize,
4053    deferred_nodes: FastDeferredNodeId,
4054    diagnostics: DiagnosticSeqId,
4055    consumed_eof: bool,
4056}
4057
4058enum FastRepetitionWork {
4059    Enter(FastRepetitionPath),
4060    Exit(FastRepetitionPath),
4061}
4062
4063/// Dense entered/exited coordinate sets for one repetition walk.
4064///
4065/// The start coordinate stays inline so short loops avoid a heap allocation;
4066/// later token indexes use one byte each instead of two hash-table entries.
4067struct FastRepetitionCoordinates {
4068    base_index: usize,
4069    base_state: u8,
4070    later_states: Vec<u8>,
4071}
4072
4073impl FastRepetitionCoordinates {
4074    const ENTERED: u8 = 0;
4075    const EXITED: u8 = 2;
4076
4077    const fn new(base_index: usize) -> Self {
4078        Self {
4079            base_index,
4080            base_state: 0,
4081            later_states: Vec::new(),
4082        }
4083    }
4084
4085    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4086        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4087    }
4088
4089    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4090        self.insert(path.index, path.consumed_eof, Self::EXITED)
4091    }
4092
4093    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4094        let Some(offset) = index.checked_sub(self.base_index) else {
4095            return false;
4096        };
4097        let state = if offset == 0 {
4098            &mut self.base_state
4099        } else {
4100            if self.later_states.len() < offset {
4101                self.later_states.resize(offset, 0);
4102            }
4103            &mut self.later_states[offset - 1]
4104        };
4105        let bit = 1 << (base_bit + u8::from(consumed_eof));
4106        let is_new = *state & bit == 0;
4107        *state |= bit;
4108        is_new
4109    }
4110}
4111
4112fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4113    if state.precedence_rule_decision()
4114        || !matches!(
4115            state.kind(),
4116            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4117        )
4118        || state.transitions().len() != 2
4119    {
4120        return None;
4121    }
4122    let mut enter = None;
4123    let mut exit = None;
4124    for (index, transition) in state.transitions().iter().enumerate() {
4125        if transition.kind() != ParserTransitionKind::Epsilon {
4126            return None;
4127        }
4128        let target = transition.target();
4129        if atn
4130            .state(target)
4131            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4132        {
4133            if exit.replace((index, target)).is_some() {
4134                return None;
4135            }
4136        } else if enter.replace((index, target)).is_some() {
4137            return None;
4138        }
4139    }
4140    let (enter_transition_index, enter_target) = enter?;
4141    let (exit_transition_index, exit_target) = exit?;
4142    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4143        atn.state(exit_target)?.loop_back_state()?
4144    } else {
4145        state.state_number()
4146    };
4147    Some(FastRepetitionShape {
4148        enter_target,
4149        exit_target,
4150        body_stop_state,
4151        enter_transition_index,
4152        exit_transition_index,
4153    })
4154}
4155
4156fn push_fast_repetition_work(
4157    work: &mut Vec<FastRepetitionWork>,
4158    shape: FastRepetitionShape,
4159    path: FastRepetitionPath,
4160    lookahead: Option<&DecisionLookahead>,
4161    symbol: i32,
4162) {
4163    // Match the normal recognizer's FIRST-set pruning before queueing work.
4164    // Ambiguous body paths still share the coordinate bitmap below.
4165    let transition_is_viable = |transition_index: usize| {
4166        let Some(entry) = lookahead else {
4167            return true;
4168        };
4169        let Some(transition) = entry.transitions.get(transition_index) else {
4170            return true;
4171        };
4172        transition.nullable || transition.symbols.contains(symbol)
4173    };
4174    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4175    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4176    if shape.enter_transition_index < shape.exit_transition_index {
4177        if exit_is_viable {
4178            work.push(FastRepetitionWork::Exit(path));
4179        }
4180        if enter_is_viable {
4181            work.push(FastRepetitionWork::Enter(path));
4182        }
4183    } else {
4184        if enter_is_viable {
4185            work.push(FastRepetitionWork::Enter(path));
4186        }
4187        if exit_is_viable {
4188            work.push(FastRepetitionWork::Exit(path));
4189        }
4190    }
4191}
4192
4193/// Memo key for the fast recognizer. `recovery_symbols` must come from
4194/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4195/// key, so equal sets share one allocation and the key can store that
4196/// allocation's address instead of cloning an `Rc` and walking the full
4197/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4198/// into distinct cache coordinates.
4199#[derive(Clone, Debug)]
4200struct FastRecognizeKey {
4201    state_number: usize,
4202    stop_state: usize,
4203    index: usize,
4204    rule_start_index: usize,
4205    decision_start_index: Option<usize>,
4206    precedence: i32,
4207    recovery_symbols_id: usize,
4208    recovery_state: Option<usize>,
4209}
4210
4211impl PartialEq for FastRecognizeKey {
4212    fn eq(&self, other: &Self) -> bool {
4213        if self.state_number != other.state_number
4214            || self.stop_state != other.stop_state
4215            || self.index != other.index
4216            || self.rule_start_index != other.rule_start_index
4217            || self.decision_start_index != other.decision_start_index
4218            || self.precedence != other.precedence
4219            || self.recovery_state != other.recovery_state
4220            || self.recovery_symbols_id != other.recovery_symbols_id
4221        {
4222            return false;
4223        }
4224        true
4225    }
4226}
4227
4228impl Eq for FastRecognizeKey {}
4229
4230impl Hash for FastRecognizeKey {
4231    fn hash<H: Hasher>(&self, hasher: &mut H) {
4232        self.state_number.hash(hasher);
4233        self.stop_state.hash(hasher);
4234        self.index.hash(hasher);
4235        self.rule_start_index.hash(hasher);
4236        self.decision_start_index.hash(hasher);
4237        self.precedence.hash(hasher);
4238        self.recovery_state.hash(hasher);
4239        self.recovery_symbols_id.hash(hasher);
4240    }
4241}
4242
4243struct FastRecoveryRequest<'a, 'b> {
4244    atn: &'a Atn,
4245    transition: ParserTransition<'a>,
4246    expected_symbols: Rc<BTreeSet<i32>>,
4247    target: usize,
4248    request: FastRecognizeRequest,
4249    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4250    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4251    expected: &'b mut ExpectedTokens,
4252}
4253
4254struct FastCurrentTokenDeletionRequest<'a, 'b> {
4255    atn: &'a Atn,
4256    expected_symbols: Rc<BTreeSet<i32>>,
4257    request: FastRecognizeRequest,
4258    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4259    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4260    expected: &'b mut ExpectedTokens,
4261}
4262
4263#[derive(Clone, Copy)]
4264struct FastChildRuleFailureRecoveryRequest<'a> {
4265    atn: &'a Atn,
4266    rule_index: usize,
4267    start_index: usize,
4268    follow_state: usize,
4269    stop_state: usize,
4270    expected: &'a ExpectedTokens,
4271}
4272
4273struct RecoveryRequest<'a, 'b> {
4274    atn: &'a Atn,
4275    transition: ParserTransition<'a>,
4276    expected_symbols: BTreeSet<i32>,
4277    target: usize,
4278    request: RecognizeRequest<'a>,
4279    visiting: &'b mut BTreeSet<RecognizeKey>,
4280    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4281    expected: &'b mut ExpectedTokens,
4282}
4283
4284struct CurrentTokenDeletionRequest<'a, 'b> {
4285    atn: &'a Atn,
4286    expected_symbols: BTreeSet<i32>,
4287    request: RecognizeRequest<'a>,
4288    visiting: &'b mut BTreeSet<RecognizeKey>,
4289    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4290    expected: &'b mut ExpectedTokens,
4291}
4292
4293/// Carries the state needed after the normal token-recovery strategies fail
4294/// for a consuming transition.
4295struct ConsumingFailureFallback<'a> {
4296    atn: &'a Atn,
4297    target: usize,
4298    request: RecognizeRequest<'a>,
4299    symbol: i32,
4300    expected_symbols: BTreeSet<i32>,
4301    decision_start_index: Option<usize>,
4302    decision: Option<usize>,
4303}
4304
4305/// Captures the parent-rule context needed when a called rule fails before it
4306/// can produce a normal outcome.
4307struct ChildRuleFailureRecovery<'a> {
4308    atn: &'a Atn,
4309    rule_index: usize,
4310    start_index: usize,
4311    follow_state: usize,
4312    stop_state: usize,
4313    member_values: BTreeMap<usize, i64>,
4314    expected: &'a ExpectedTokens,
4315}
4316
4317/// Bundles the context needed to evaluate one semantic predicate transition.
4318#[derive(Clone, Copy, Debug)]
4319struct PredicateEval<'a> {
4320    index: usize,
4321    rule_index: usize,
4322    pred_index: usize,
4323    predicates: &'a [(usize, usize, ParserPredicate)],
4324    semantics: Option<&'a ParserSemantics>,
4325    context: Option<&'a ParserRuleContext>,
4326    local_int_arg: Option<(usize, i64)>,
4327    member_values: &'a BTreeMap<usize, i64>,
4328}
4329
4330#[derive(Clone, Copy, Debug)]
4331struct ParserSemanticHookRequest<'a> {
4332    index: usize,
4333    rule_index: usize,
4334    pred_index: usize,
4335    context: Option<&'a ParserRuleContext>,
4336    local_int_arg: Option<(usize, i64)>,
4337    member_values: &'a BTreeMap<usize, i64>,
4338}
4339
4340/// Predicate-evaluation context over the recognizer's speculative state.
4341///
4342/// This sits in the prediction hot loop, so everything is borrowed: member
4343/// state read-only from the current speculative path and the rule name
4344/// straight from recognizer metadata. Predicates are pure by construction
4345/// ([`semir::PExpr`] has no mutating node); statement execution uses
4346/// [`ParserTableSemCtx`] (speculative member/return replay) and
4347/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4348struct ParserSemIrCtx<'a, S, H>
4349where
4350    S: TokenSource,
4351    H: SemanticHooks,
4352{
4353    input: &'a mut CommonTokenStream<S>,
4354    tree_storage: &'a ParseTreeStorage,
4355    semantic_hooks: &'a mut H,
4356    rule_index: usize,
4357    coordinate_index: usize,
4358    rule_name: Option<&'a str>,
4359    context: Option<&'a ParserRuleContext>,
4360    local_int_arg: Option<(usize, i64)>,
4361    member_values: &'a BTreeMap<usize, i64>,
4362    invoked_predicates: &'a mut Vec<(usize, usize)>,
4363    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4364    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4365    /// table path instead of coercing the miss to `false`.
4366    unknown_predicate_policy: UnknownSemanticPolicy,
4367    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4368}
4369
4370impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4371where
4372    S: TokenSource,
4373    H: SemanticHooks,
4374{
4375    type TokenText<'a>
4376        = TokenView<'a>
4377    where
4378        Self: 'a;
4379
4380    fn la(&mut self, offset: isize) -> i64 {
4381        i64::from(self.input.la(offset))
4382    }
4383
4384    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4385        self.input.lt(offset)
4386    }
4387
4388    fn token_index_adjacent(&mut self) -> bool {
4389        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4390            return false;
4391        };
4392        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4393            return false;
4394        };
4395        first + 1 == second
4396    }
4397
4398    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4399        self.context.and_then(|context| {
4400            context
4401                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4402                .next()
4403                .map(crate::tree::RuleNodeView::text)
4404        })
4405    }
4406
4407    fn member(&self, member: usize) -> Option<i64> {
4408        Some(self.member_values.get(&member).copied().unwrap_or_default())
4409    }
4410
4411    fn local_arg(&self) -> Option<i64> {
4412        self.local_int_arg.map(|(_, value)| value)
4413    }
4414
4415    fn column(&self) -> Option<i64> {
4416        None
4417    }
4418
4419    fn token_start_column(&self) -> Option<i64> {
4420        None
4421    }
4422
4423    fn token_text_so_far(&self) -> Option<String> {
4424        None
4425    }
4426
4427    fn hook(&mut self, _hook: HookId) -> bool {
4428        let mut ctx = ParserSemCtx {
4429            input: &mut *self.input,
4430            tree_storage: self.tree_storage,
4431            rule_index: self.rule_index,
4432            coordinate_index: self.coordinate_index,
4433            rule_name: self.rule_name.map(str::to_owned),
4434            context: self.context,
4435            tree: None,
4436            local_int_arg: self.local_int_arg,
4437            member_values: self.member_values,
4438            action: None,
4439        };
4440        match self
4441            .semantic_hooks
4442            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4443        {
4444            Some(result) => result,
4445            // No hook answered this coordinate: fall through to the configured
4446            // policy instead of silently rejecting the alternative, matching the
4447            // legacy table path's dispatch chain (hook → policy).
4448            None => apply_unknown_predicate_policy(
4449                self.unknown_predicate_policy,
4450                self.rule_index,
4451                self.coordinate_index,
4452                self.unknown_predicate_hits,
4453            ),
4454        }
4455    }
4456
4457    fn trace_bool(&mut self, value: bool) -> bool {
4458        let key = (self.rule_index, self.coordinate_index);
4459        if !self.invoked_predicates.contains(&key) {
4460            self.invoked_predicates.push(key);
4461            use std::io::Write as _;
4462            let mut stdout = std::io::stdout().lock();
4463            let _ = writeln!(stdout, "eval={value}");
4464        }
4465        value
4466    }
4467}
4468
4469/// Captures predicate-failure recovery metadata for fail-option predicates.
4470struct PredicateFailureRecovery<'a> {
4471    rule_index: usize,
4472    index: usize,
4473    message: &'a str,
4474    member_values: BTreeMap<usize, i64>,
4475    return_values: BTreeMap<String, i64>,
4476    rule_alt_number: usize,
4477}
4478
4479#[derive(Debug)]
4480enum DirectAdaptiveParseControl {
4481    Fallback(DirectAdaptiveFallback),
4482}
4483
4484#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4485enum DirectAdaptiveFallback {
4486    Action,
4487    InvalidAlt,
4488    LeftRecursiveBoundary,
4489    MissingAtn,
4490    NoTransition,
4491    Predicate,
4492    Prediction,
4493    Precedence,
4494    RuleStop,
4495    SemanticContext,
4496    StepLimit,
4497    TokenMismatch,
4498    UnknownDecision,
4499}
4500
4501type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4502
4503struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4504where
4505    S: TokenSource,
4506    H: SemanticHooks,
4507{
4508    parser: &'sim mut BaseParser<S, H>,
4509    atn: &'atn Atn,
4510    simulator: &'sim mut ParserAtnSimulator<'atn>,
4511    decision_by_state: Vec<Option<usize>>,
4512    steps: usize,
4513}
4514
4515/// Outcome of a generated token / set / not-set match that may recover.
4516///
4517/// Generated parsers append `children` to the current rule context. `consumed_eof`
4518/// reports whether the match actually consumed a real EOF terminal — it is true
4519/// only on a successful match (or single-token deletion that lands on EOF), and
4520/// always false on single-token insertion, which synthesizes a missing token and
4521/// consumes nothing. Generated code feeds this into `finish_rule`'s
4522/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4523/// truly matched, matching ANTLR's `matchedEOF` semantics.
4524#[derive(Clone, Debug, Eq, PartialEq)]
4525pub struct GeneratedMatch {
4526    children: GeneratedMatchChildren,
4527    consumed_eof: bool,
4528}
4529
4530#[derive(Clone, Copy)]
4531enum GeneratedExpectedSymbols<'a> {
4532    Tree(&'a BTreeSet<i32>),
4533    TokenSet(ParserIntervalSet<'a>),
4534    TokenSetComplement {
4535        set: ParserIntervalSet<'a>,
4536        min_vocabulary: i32,
4537        max_vocabulary: i32,
4538    },
4539}
4540
4541impl GeneratedExpectedSymbols<'_> {
4542    fn is_empty(self) -> bool {
4543        match self {
4544            Self::Tree(symbols) => symbols.is_empty(),
4545            Self::TokenSet(set) => set.is_empty(),
4546            Self::TokenSetComplement {
4547                set,
4548                min_vocabulary,
4549                max_vocabulary,
4550            } => (min_vocabulary..=max_vocabulary).all(|symbol| set.contains(symbol)),
4551        }
4552    }
4553
4554    fn first(self) -> Option<i32> {
4555        match self {
4556            Self::Tree(symbols) => symbols.iter().next().copied(),
4557            Self::TokenSet(set) => set.ranges().next().map(|(start, _)| start),
4558            Self::TokenSetComplement {
4559                set,
4560                min_vocabulary,
4561                max_vocabulary,
4562            } => (min_vocabulary..=max_vocabulary).find(|symbol| !set.contains(*symbol)),
4563        }
4564    }
4565
4566    fn display(self, vocabulary: &Vocabulary) -> String {
4567        match self {
4568            Self::Tree(symbols) => expected_symbols_display(symbols, vocabulary),
4569            Self::TokenSet(set) => expected_symbols_display_iter(
4570                set.ranges().flat_map(|(start, stop)| start..=stop),
4571                vocabulary,
4572            ),
4573            Self::TokenSetComplement {
4574                set,
4575                min_vocabulary,
4576                max_vocabulary,
4577            } => expected_symbols_display_iter(
4578                (min_vocabulary..=max_vocabulary).filter(|symbol| !set.contains(*symbol)),
4579                vocabulary,
4580            ),
4581        }
4582    }
4583}
4584
4585#[derive(Clone, Debug, Eq, PartialEq)]
4586enum GeneratedMatchChildren {
4587    One(ParseTree),
4588    Many(Vec<ParseTree>),
4589}
4590
4591struct GeneratedMatchChildrenIntoIter {
4592    one: Option<ParseTree>,
4593    many: Option<std::vec::IntoIter<ParseTree>>,
4594}
4595
4596impl Iterator for GeneratedMatchChildrenIntoIter {
4597    type Item = ParseTree;
4598
4599    fn next(&mut self) -> Option<Self::Item> {
4600        self.one
4601            .take()
4602            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4603    }
4604}
4605
4606impl GeneratedMatch {
4607    /// Parse-tree children produced by the match (the matched terminal, an
4608    /// error node plus deleted-then-matched terminal, or a single missing-token
4609    /// error node).
4610    #[must_use]
4611    pub fn children(&self) -> &[ParseTree] {
4612        match &self.children {
4613            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4614            GeneratedMatchChildren::Many(children) => children,
4615        }
4616    }
4617
4618    /// Consumes the result, returning the children for appending to the rule
4619    /// context.
4620    #[must_use]
4621    pub fn into_children(self) -> Vec<ParseTree> {
4622        match self.children {
4623            GeneratedMatchChildren::One(child) => vec![child],
4624            GeneratedMatchChildren::Many(children) => children,
4625        }
4626    }
4627
4628    /// Consumes the match without allocating for the common single-child case.
4629    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4630        match self.children {
4631            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4632                one: Some(child),
4633                many: None,
4634            },
4635            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4636                one: None,
4637                many: Some(children.into_iter()),
4638            },
4639        }
4640    }
4641
4642    /// Whether a real EOF terminal was consumed by this match.
4643    #[must_use]
4644    pub const fn consumed_eof(&self) -> bool {
4645        self.consumed_eof
4646    }
4647}
4648
4649impl<S> BaseParser<S, NoSemanticHooks>
4650where
4651    S: TokenSource,
4652{
4653    /// Creates a parser base over a buffered token stream and recognizer
4654    /// metadata.
4655    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4656        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4657    }
4658}
4659
4660impl<S, H> BaseParser<S, H>
4661where
4662    S: TokenSource,
4663    H: SemanticHooks,
4664{
4665    /// Creates a parser base with caller-owned semantic hooks.
4666    pub fn with_semantic_hooks(
4667        input: CommonTokenStream<S>,
4668        data: RecognizerData,
4669        semantic_hooks: H,
4670    ) -> Self {
4671        Self {
4672            input,
4673            tree: ParseTreeStorage::new(),
4674            data,
4675            semantic_hooks,
4676            build_parse_trees: true,
4677            syntax_errors: 0,
4678            report_diagnostic_errors: false,
4679            prediction_mode: PredictionMode::Ll,
4680            prediction_diagnostics: Vec::new(),
4681            reported_prediction_diagnostics: BTreeSet::new(),
4682            generated_parser_diagnostics: Vec::new(),
4683            generated_sync_expected: None,
4684            int_members: BTreeMap::new(),
4685            rule_context_stack: Vec::new(),
4686            rule_context_version: 0,
4687            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4688            pending_invoking_states: Vec::new(),
4689            precedence_stack: vec![0],
4690            invoked_predicates: Vec::new(),
4691            bail_on_error: false,
4692            unknown_predicate_policy: UnknownSemanticPolicy::default(),
4693            unknown_predicate_hits: Vec::new(),
4694            unhandled_action_hits: Vec::new(),
4695            rule_first_set_cache: Vec::new(),
4696            state_expected_cache: FxHashMap::default(),
4697            state_expected_token_cache: FxHashMap::default(),
4698            rule_stop_reach_cache: Vec::new(),
4699            recovery_symbols_intern: FxHashMap::default(),
4700            decision_lookahead_cache: FxHashMap::default(),
4701            ll1_decision_cache: FxHashMap::default(),
4702            fast_predicate_cache: FxHashMap::default(),
4703            empty_cycle_cache: Vec::new(),
4704            empty_cycle_cache_atn: None,
4705            clean_memo_mode: CleanMemoMode::Probe,
4706            clean_memo_probe_seen: FxHashSet::default(),
4707            clean_memo_probe_samples: 0,
4708            clean_memo_probe_repeats: 0,
4709            clean_memo_sparse_samples: 0,
4710            fast_recognize_scratch: FastRecognizeTopScratch::default(),
4711            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
4712            empty_recovery_symbols: Rc::new(BTreeSet::new()),
4713            fast_first_set_prefilter: true,
4714            fast_recovery_enabled: true,
4715            fast_token_nodes_enabled: true,
4716            recognition_arena: RecognitionArena::default(),
4717            last_recognition_arena_root: NodeSeqId::EMPTY,
4718            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
4719        }
4720    }
4721
4722    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
4723        &mut self.input
4724    }
4725
4726    /// Fully resets parser-owned state and rewinds the current token stream.
4727    ///
4728    /// Parser configuration, semantic hooks, learned DFA tables, and
4729    /// grammar-owned member values are retained.
4730    pub fn reset(&mut self) {
4731        self.input.seek(0);
4732        self.tree.reset();
4733        self.data.set_state(-1);
4734        self.syntax_errors = 0;
4735        self.prediction_diagnostics.clear();
4736        self.reported_prediction_diagnostics.clear();
4737        self.generated_parser_diagnostics.clear();
4738        self.generated_sync_expected = None;
4739        self.rule_context_stack.clear();
4740        self.advance_rule_context_version();
4741        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
4742        self.pending_invoking_states.clear();
4743        self.precedence_stack.clear();
4744        self.precedence_stack.push(0);
4745        self.invoked_predicates.clear();
4746        self.unknown_predicate_hits.clear();
4747        self.unhandled_action_hits.clear();
4748        self.reset_per_parse_caches();
4749        self.fast_first_set_prefilter = true;
4750        self.fast_recovery_enabled = true;
4751        self.fast_token_nodes_enabled = self.build_parse_trees;
4752        self.reset_recognition_arena();
4753    }
4754
4755    /// Replaces the buffered token stream and fully resets this parser.
4756    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
4757        self.input = input;
4758        self.reset();
4759    }
4760
4761    /// Installs the policy for predicate coordinates that no translated table
4762    /// entry or user hook resolves.
4763    ///
4764    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
4765    /// but generated recursive-descent rules evaluate predicates directly
4766    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
4767    /// going through those options. Generated parser constructors call this so
4768    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
4769    /// the field at its `AssumeTrue` default and silently accepting an
4770    /// unimplemented hook predicate.
4771    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
4772        self.unknown_predicate_policy = policy;
4773    }
4774
4775    /// Reports any unknown predicate coordinate the generated-direct path
4776    /// recorded under [`UnknownSemanticPolicy::Error`], as an
4777    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
4778    /// after a rule completes so the fail-loud policy surfaces on the
4779    /// generated path the same way the interpreter entry surfaces it.
4780    #[must_use]
4781    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
4782        let error = self.unknown_semantic_error();
4783        self.unknown_predicate_hits.clear();
4784        self.unhandled_action_hits.clear();
4785        error
4786    }
4787
4788    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
4789    ///
4790    /// Generated parsers call this at the true top-level entry so a parser
4791    /// reused after a fail-loud (or recovered) parse starts clean, without
4792    /// clearing hits mid-parse where a generated parent still needs a child's
4793    /// recorded coordinate to survive to the top-level boundary.
4794    pub fn reset_unknown_semantic_hits(&mut self) {
4795        self.unknown_predicate_hits.clear();
4796        self.unhandled_action_hits.clear();
4797    }
4798
4799    /// Returns the token stream owned by this parser.
4800    #[must_use]
4801    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
4802        &self.input
4803    }
4804
4805    /// Returns the token stream for source replacement or in-place re-feeding.
4806    #[must_use]
4807    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
4808        &mut self.input
4809    }
4810
4811    /// Returns the canonical token store referenced by parse trees.
4812    #[must_use]
4813    pub const fn token_store(&self) -> &TokenStore {
4814        self.input.token_store()
4815    }
4816
4817    /// Returns the flat CST storage populated by completed rules.
4818    #[must_use]
4819    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
4820        &self.tree
4821    }
4822
4823    /// Resolves a compact parse-tree ID into a borrowing node view.
4824    #[must_use]
4825    pub fn node(&self, id: NodeId) -> Node<'_> {
4826        self.tree
4827            .node(self.input.token_store(), id)
4828            .expect("parser-produced node ID should remain valid")
4829    }
4830
4831    /// Consumes this parser and returns its token stream.
4832    #[must_use]
4833    pub fn into_token_stream(self) -> CommonTokenStream<S> {
4834        self.input
4835    }
4836
4837    /// Consumes this parser and returns its canonical token store.
4838    #[must_use]
4839    pub fn into_token_store(self) -> TokenStore {
4840        self.input.into_token_store()
4841    }
4842
4843    /// Consumes the parser and pairs its token store and flat CST with `root`.
4844    #[must_use]
4845    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
4846        ParsedFile::new(self.input.into_token_store(), self.tree, root)
4847    }
4848
4849    /// Returns the number of parser syntax errors recorded by committed parse
4850    /// paths so far.
4851    pub const fn number_of_syntax_errors(&self) -> usize {
4852        self.syntax_errors
4853    }
4854
4855    /// Computes reachability and retained-capacity counters for the most recent
4856    /// interpreted-rule recognition arena.
4857    ///
4858    /// The reachability scan is linear in the arena size and is deferred until
4859    /// this instrumentation method is called.
4860    #[must_use]
4861    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
4862        self.recognition_arena.stats(
4863            self.last_recognition_arena_root,
4864            self.last_recognition_arena_diagnostics,
4865        )
4866    }
4867
4868    /// Records a syntax error that generated parser code returns as fatal before
4869    /// it can recover into the current rule context.
4870    pub const fn record_generated_syntax_error(&mut self) {
4871        self.record_syntax_errors(1);
4872    }
4873
4874    const fn record_syntax_errors(&mut self, count: usize) {
4875        self.syntax_errors = self.syntax_errors.saturating_add(count);
4876    }
4877
4878    /// Emits diagnostics buffered by the token stream while generated parser
4879    /// code was fetching lexer tokens directly.
4880    pub fn report_token_source_errors(&mut self) {
4881        let errors = self.input.drain_source_errors();
4882        self.dispatch_token_source_errors(&errors);
4883    }
4884
4885    /// Captures generated-parser diagnostics and syntax-error count before a
4886    /// speculative generated rule path.
4887    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
4888        GeneratedDiagnosticsCheckpoint {
4889            diagnostics_len: self.generated_parser_diagnostics.len(),
4890            syntax_errors: self.syntax_errors,
4891            tree: self.tree.checkpoint(),
4892        }
4893    }
4894
4895    /// Restores generated-parser diagnostics after a speculative rule path failed.
4896    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
4897        self.generated_parser_diagnostics
4898            .truncate(marker.diagnostics_len);
4899        self.syntax_errors = marker.syntax_errors;
4900        self.generated_sync_expected = None;
4901        self.tree.rollback(marker.tree);
4902    }
4903
4904    /// Emits diagnostics recorded by committed generated parser recovery.
4905    pub fn report_generated_parser_diagnostics(&mut self) {
4906        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
4907        let token_errors = self.input.drain_source_errors();
4908        self.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
4909    }
4910
4911    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
4912        self.notify_error_listeners(
4913            diagnostic.line,
4914            diagnostic.column,
4915            &diagnostic.message,
4916            None,
4917        );
4918    }
4919
4920    fn dispatch_parser_diagnostics<'a>(
4921        &self,
4922        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
4923    ) {
4924        for diagnostic in diagnostics {
4925            self.dispatch_parser_diagnostic(diagnostic);
4926        }
4927    }
4928
4929    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
4930        if self.input.token_source().report_error(source_error) {
4931            return;
4932        }
4933        self.notify_error_listeners(
4934            source_error.line,
4935            source_error.column,
4936            &source_error.message,
4937            None,
4938        );
4939    }
4940
4941    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
4942        for error in errors {
4943            self.dispatch_token_source_error(error);
4944        }
4945    }
4946
4947    /// Dispatches generated parser and lexer diagnostics in the same
4948    /// source-position order as ANTLR's lazy token stream reports them.
4949    fn dispatch_generated_diagnostics(
4950        &self,
4951        parser_diagnostics: &[ParserDiagnostic],
4952        token_errors: &[TokenSourceError],
4953    ) {
4954        // Parser diagnostics keep their event order: Java's console and
4955        // DiagnosticErrorListener print reports as prediction produces them,
4956        // so reportAttemptingFullContext precedes reportContextSensitivity
4957        // even though the latter's position is earlier. Buffered token-source
4958        // errors interleave by source position and win ties.
4959        let mut token_iter = token_errors.iter().peekable();
4960        for diagnostic in parser_diagnostics {
4961            while let Some(error) = token_iter.peek() {
4962                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
4963                    self.dispatch_token_source_error(error);
4964                    token_iter.next();
4965                } else {
4966                    break;
4967                }
4968            }
4969            self.dispatch_parser_diagnostic(diagnostic);
4970        }
4971        for error in token_iter {
4972            self.dispatch_token_source_error(error);
4973        }
4974    }
4975
4976    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
4977    /// decision code.
4978    pub fn record_generated_ambiguity_diagnostic(
4979        &mut self,
4980        atn: &Atn,
4981        state_number: usize,
4982        start_index: usize,
4983        stop_index: usize,
4984        alts: &[usize],
4985    ) {
4986        if !self.report_diagnostic_errors || alts.len() < 2 {
4987            return;
4988        }
4989        let Some(decision) = atn
4990            .decision_to_state()
4991            .iter()
4992            .position(|candidate| candidate == state_number)
4993        else {
4994            return;
4995        };
4996        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
4997            return;
4998        };
4999        let rule_name = self
5000            .rule_names()
5001            .get(rule_index)
5002            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5003        let input = display_input_text(&self.input.text(start_index, stop_index));
5004        let alts = alts
5005            .iter()
5006            .map(usize::to_string)
5007            .collect::<Vec<_>>()
5008            .join(", ");
5009        let key = (decision, start_index, format!("{alts}:{input}"));
5010        if !self.reported_prediction_diagnostics.insert(key) {
5011            return;
5012        }
5013        let start_diagnostic = diagnostic_for_token(
5014            self.token_at(start_index),
5015            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5016        );
5017        let stop_diagnostic = diagnostic_for_token(
5018            self.token_at(stop_index),
5019            format!(
5020                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5021            ),
5022        );
5023        self.generated_parser_diagnostics.push(start_diagnostic);
5024        self.generated_parser_diagnostics.push(stop_diagnostic);
5025    }
5026
5027    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5028    /// parser calls to the adaptive simulator.
5029    pub fn record_generated_prediction_diagnostic(
5030        &mut self,
5031        atn: &Atn,
5032        state_number: usize,
5033        prediction: &ParserAtnPrediction,
5034    ) {
5035        let Some(diagnostic) = &prediction.diagnostic else {
5036            return;
5037        };
5038        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5039            return;
5040        }
5041        let Some(decision) = atn
5042            .decision_to_state()
5043            .iter()
5044            .position(|candidate| candidate == state_number)
5045        else {
5046            return;
5047        };
5048        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5049            return;
5050        };
5051        let rule_name = self
5052            .rule_names()
5053            .get(rule_index)
5054            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5055        let attempt_input = display_input_text(
5056            &self
5057                .input
5058                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5059        );
5060        let result_input = display_input_text(
5061            &self
5062                .input
5063                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5064        );
5065        let alts = diagnostic
5066            .conflicting_alts
5067            .iter()
5068            .map(usize::to_string)
5069            .collect::<Vec<_>>()
5070            .join(", ");
5071        let key = (
5072            decision,
5073            diagnostic.start_index,
5074            format!(
5075                "{:?}:{alts}:{attempt_input}:{result_input}",
5076                diagnostic.kind
5077            ),
5078        );
5079        if !self.reported_prediction_diagnostics.insert(key) {
5080            return;
5081        }
5082        let attempt_diagnostic = diagnostic_for_token(
5083            self.token_at(diagnostic.sll_stop_index),
5084            format!(
5085                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5086            ),
5087        );
5088        self.generated_parser_diagnostics.push(attempt_diagnostic);
5089        let message = match diagnostic.kind {
5090            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5091                // Java's DiagnosticErrorListener is exactOnly by default:
5092                // non-exact ambiguities (default LL mode stopping at the
5093                // first resolvable conflict) report the attempt above but
5094                // suppress the ambiguity line itself.
5095                if !diagnostic.exact {
5096                    return;
5097                }
5098                format!(
5099                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5100                )
5101            }
5102            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5103                format!(
5104                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5105                )
5106            }
5107        };
5108        let result_diagnostic =
5109            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5110        self.generated_parser_diagnostics.push(result_diagnostic);
5111    }
5112
5113    pub fn la(&self, offset: isize) -> i32 {
5114        self.input.la_token(offset)
5115    }
5116
5117    pub fn consume(&mut self) {
5118        IntStream::consume(&mut self.input);
5119    }
5120
5121    /// Sets a generated integer member value used by target-template tests.
5122    pub fn set_int_member(&mut self, member: usize, value: i64) {
5123        self.int_members.insert(member, value);
5124    }
5125
5126    /// Reads a generated integer member value.
5127    pub fn int_member(&self, member: usize) -> Option<i64> {
5128        self.int_members.get(&member).copied()
5129    }
5130
5131    /// Captures generated integer members before speculative generated parser
5132    /// execution.
5133    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
5134        self.int_members.clone()
5135    }
5136
5137    /// Restores generated integer members after generated parser fallback.
5138    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
5139        self.int_members = members;
5140    }
5141
5142    /// Adds `delta` to a generated integer member and returns the new value.
5143    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5144        let value = self.int_members.entry(member).or_default();
5145        *value += delta;
5146        *value
5147    }
5148
5149    fn token_type_for_id(&self, id: TokenId) -> i32 {
5150        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5151    }
5152
5153    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5154        if self.build_parse_trees {
5155            self.tree.terminal(id)
5156        } else {
5157            NodeId::placeholder()
5158        }
5159    }
5160
5161    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5162        if self.build_parse_trees {
5163            self.tree.error(id)
5164        } else {
5165            NodeId::placeholder()
5166        }
5167    }
5168
5169    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5170        context.set_start_id(id);
5171    }
5172
5173    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5174        context.set_stop_id(id);
5175    }
5176
5177    fn insert_synthetic_token(
5178        &mut self,
5179        token_type: i32,
5180        text: String,
5181        line: usize,
5182        column: usize,
5183    ) -> Result<TokenId, AntlrError> {
5184        self.input
5185            .insert(
5186                TokenSpec::explicit(token_type, text)
5187                    .with_span(usize::MAX, usize::MAX)
5188                    .with_byte_span(0, 0)
5189                    .with_position(line, column),
5190            )
5191            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5192    }
5193
5194    /// Matches and consumes the current token when it has the expected token
5195    /// type.
5196    ///
5197    /// On success the consumed token is wrapped as a terminal parse-tree node.
5198    /// On mismatch the error carries vocabulary display names so diagnostics are
5199    /// stable across literal and symbolic token naming.
5200    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5201        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5202            line: 0,
5203            column: 0,
5204            message: "missing current token".to_owned(),
5205        })?;
5206        let current_type = self.token_type_for_id(current);
5207        if current_type == token_type {
5208            self.consume();
5209            Ok(self.terminal_tree(current))
5210        } else {
5211            Err(AntlrError::MismatchedInput {
5212                expected: self.vocabulary().display_name(token_type),
5213                found: self.vocabulary().display_name(current_type),
5214            })
5215        }
5216    }
5217
5218    /// Matches a token from generated recursive-descent code, including ANTLR's
5219    /// single-token insertion recovery when the active rule context can legally
5220    /// continue at the current input symbol.
5221    pub fn match_token_recovering(
5222        &mut self,
5223        token_type: i32,
5224        follow_state: usize,
5225        atn: &Atn,
5226    ) -> Result<GeneratedMatch, AntlrError> {
5227        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5228            line: 0,
5229            column: 0,
5230            message: "missing current token".to_owned(),
5231        })?;
5232        let current_type = self.token_type_for_id(current);
5233        if current_type == token_type {
5234            self.generated_sync_expected = None;
5235            let consumed_eof = current_type == TOKEN_EOF;
5236            self.consume();
5237            return Ok(GeneratedMatch {
5238                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5239                consumed_eof,
5240            });
5241        }
5242        let mut expected_symbols = BTreeSet::new();
5243        expected_symbols.insert(token_type);
5244        self.recover_generated_match(
5245            current,
5246            GeneratedExpectedSymbols::Tree(&expected_symbols),
5247            follow_state,
5248            atn,
5249            |symbol| symbol == token_type,
5250        )
5251    }
5252
5253    pub fn match_set_recovering(
5254        &mut self,
5255        intervals: &[(i32, i32)],
5256        follow_state: usize,
5257        atn: &Atn,
5258    ) -> Result<GeneratedMatch, AntlrError> {
5259        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5260            line: 0,
5261            column: 0,
5262            message: "missing current token".to_owned(),
5263        })?;
5264        let current_type = self.token_type_for_id(current);
5265        if interval_set_contains(intervals, current_type) {
5266            self.generated_sync_expected = None;
5267            let consumed_eof = current_type == TOKEN_EOF;
5268            self.consume();
5269            return Ok(GeneratedMatch {
5270                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5271                consumed_eof,
5272            });
5273        }
5274        let expected_symbols = interval_symbols(intervals);
5275        self.recover_generated_match(
5276            current,
5277            GeneratedExpectedSymbols::Tree(&expected_symbols),
5278            follow_state,
5279            atn,
5280            |symbol| interval_set_contains(intervals, symbol),
5281        )
5282    }
5283
5284    pub fn match_token_set_recovering(
5285        &mut self,
5286        set: ParserIntervalSet<'_>,
5287        follow_state: usize,
5288        atn: &Atn,
5289    ) -> Result<GeneratedMatch, AntlrError> {
5290        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5291            line: 0,
5292            column: 0,
5293            message: "missing current token".to_owned(),
5294        })?;
5295        let current_type = self.token_type_for_id(current);
5296        if set.contains(current_type) {
5297            self.generated_sync_expected = None;
5298            let consumed_eof = current_type == TOKEN_EOF;
5299            self.consume();
5300            return Ok(GeneratedMatch {
5301                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5302                consumed_eof,
5303            });
5304        }
5305        self.recover_generated_match(
5306            current,
5307            GeneratedExpectedSymbols::TokenSet(set),
5308            follow_state,
5309            atn,
5310            |symbol| set.contains(symbol),
5311        )
5312    }
5313
5314    pub fn match_not_set_recovering(
5315        &mut self,
5316        intervals: &[(i32, i32)],
5317        min_vocabulary: i32,
5318        max_vocabulary: i32,
5319        follow_state: usize,
5320        atn: &Atn,
5321    ) -> Result<GeneratedMatch, AntlrError> {
5322        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5323            line: 0,
5324            column: 0,
5325            message: "missing current token".to_owned(),
5326        })?;
5327        let current_type = self.token_type_for_id(current);
5328        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5329            && !interval_set_contains(intervals, current_type)
5330        {
5331            self.generated_sync_expected = None;
5332            let consumed_eof = current_type == TOKEN_EOF;
5333            self.consume();
5334            return Ok(GeneratedMatch {
5335                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5336                consumed_eof,
5337            });
5338        }
5339        let expected_symbols =
5340            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5341        self.recover_generated_match(
5342            current,
5343            GeneratedExpectedSymbols::Tree(&expected_symbols),
5344            follow_state,
5345            atn,
5346            |symbol| {
5347                (min_vocabulary..=max_vocabulary).contains(&symbol)
5348                    && !interval_set_contains(intervals, symbol)
5349            },
5350        )
5351    }
5352
5353    pub fn match_not_token_set_recovering(
5354        &mut self,
5355        set: ParserIntervalSet<'_>,
5356        min_vocabulary: i32,
5357        max_vocabulary: i32,
5358        follow_state: usize,
5359        atn: &Atn,
5360    ) -> Result<GeneratedMatch, AntlrError> {
5361        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5362            line: 0,
5363            column: 0,
5364            message: "missing current token".to_owned(),
5365        })?;
5366        let current_type = self.token_type_for_id(current);
5367        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
5368        {
5369            self.generated_sync_expected = None;
5370            let consumed_eof = current_type == TOKEN_EOF;
5371            self.consume();
5372            return Ok(GeneratedMatch {
5373                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5374                consumed_eof,
5375            });
5376        }
5377        self.recover_generated_match(
5378            current,
5379            GeneratedExpectedSymbols::TokenSetComplement {
5380                set,
5381                min_vocabulary,
5382                max_vocabulary,
5383            },
5384            follow_state,
5385            atn,
5386            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
5387        )
5388    }
5389
5390    fn recover_generated_match(
5391        &mut self,
5392        current: TokenId,
5393        expected_symbols: GeneratedExpectedSymbols<'_>,
5394        follow_state: usize,
5395        atn: &Atn,
5396        matches: impl Fn(i32) -> bool,
5397    ) -> Result<GeneratedMatch, AntlrError> {
5398        let expected_display = expected_symbols.display(self.vocabulary());
5399        let (current_type, current_line, current_column, current_display) = {
5400            let token = self
5401                .input
5402                .token_view(current)
5403                .expect("current token ID should be valid");
5404            (
5405                token.token_type(),
5406                token.line(),
5407                token.column(),
5408                token_input_display(&token),
5409            )
5410        };
5411        if self.bail_on_error {
5412            return Err(AntlrError::ParserError {
5413                line: current_line,
5414                column: current_column,
5415                message: format!("mismatched input {current_display} expecting {expected_display}"),
5416            });
5417        }
5418        if current_type != TOKEN_EOF
5419            && let Some(next) = self.input.lt_id(2)
5420            && matches(self.token_type_for_id(next))
5421        {
5422            let message =
5423                format!("extraneous input {current_display} expecting {expected_display}");
5424            self.push_generated_parser_diagnostic(ParserDiagnostic {
5425                line: current_line,
5426                column: current_column,
5427                message,
5428            });
5429            self.record_syntax_errors(1);
5430            self.generated_sync_expected = None;
5431            // Single-token deletion: skip `current`, then accept `next`. The
5432            // accepted token can be EOF only if it is a real EOF terminal.
5433            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5434            self.consume();
5435            self.consume();
5436            return Ok(GeneratedMatch {
5437                children: GeneratedMatchChildren::Many(vec![
5438                    self.error_tree(current),
5439                    self.terminal_tree(next),
5440                ]),
5441                consumed_eof,
5442            });
5443        }
5444        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5445        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5446        // *after* the current element can consume the current symbol. At EOF that
5447        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5448        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5449        // when EOF merely leaks in from the empty enclosing context (as in
5450        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5451        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5452        // so consult the follow state's OWN expected set for the explicit case.
5453        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5454            && self
5455                .cached_state_expected_symbols(atn, follow_state)
5456                .contains(&TOKEN_EOF);
5457        if follow_symbols.contains(&current_type)
5458            && (current_type != TOKEN_EOF
5459                || self.rule_context_stack.len() > 1
5460                || expected_symbols.is_empty()
5461                || follow_explicitly_expects_eof)
5462        {
5463            let message = format!("missing {expected_display} at {current_display}");
5464            self.push_generated_parser_diagnostic(ParserDiagnostic {
5465                line: current_line,
5466                column: current_column,
5467                message,
5468            });
5469            self.record_syntax_errors(1);
5470            self.generated_sync_expected = None;
5471            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
5472            let missing_display = expected_symbol_display(token_type, self.vocabulary());
5473            let token = self.insert_synthetic_token(
5474                token_type,
5475                format!("<missing {missing_display}>"),
5476                current_line,
5477                current_column,
5478            )?;
5479            // Single-token insertion synthesizes a missing token and consumes
5480            // nothing, so no EOF terminal is consumed even when the lookahead is
5481            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5482            // from recording EOF as the rule stop on this recovery path.
5483            return Ok(GeneratedMatch {
5484                children: GeneratedMatchChildren::One(self.error_tree(token)),
5485                consumed_eof: false,
5486            });
5487        }
5488        let mismatch_expected_display = self
5489            .generated_sync_expected
5490            .take()
5491            .map_or(expected_display, |symbols| {
5492                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
5493            });
5494        Err(AntlrError::ParserError {
5495            line: current_line,
5496            column: current_column,
5497            message: format!(
5498                "mismatched input {current_display} expecting {mismatch_expected_display}"
5499            ),
5500        })
5501    }
5502
5503    fn generated_recovery_follow_symbols(
5504        &mut self,
5505        atn: &Atn,
5506        follow_state: usize,
5507    ) -> BTreeSet<i32> {
5508        let mut follow = self
5509            .cached_state_expected_symbols(atn, follow_state)
5510            .as_ref()
5511            .clone();
5512        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5513            follow.extend(self.context_expected_symbols(atn));
5514        }
5515        follow
5516    }
5517
5518    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5519        self.match_token(TOKEN_EOF)
5520    }
5521
5522    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5523        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5524    }
5525
5526    pub fn match_not_set(
5527        &mut self,
5528        intervals: &[(i32, i32)],
5529        min_vocabulary: i32,
5530        max_vocabulary: i32,
5531    ) -> Result<ParseTree, AntlrError> {
5532        self.match_interval_condition(intervals, |symbol| {
5533            (min_vocabulary..=max_vocabulary).contains(&symbol)
5534                && !interval_set_contains(intervals, symbol)
5535        })
5536    }
5537
5538    fn match_interval_condition(
5539        &mut self,
5540        intervals: &[(i32, i32)],
5541        matches: impl FnOnce(i32) -> bool,
5542    ) -> Result<ParseTree, AntlrError> {
5543        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5544            line: 0,
5545            column: 0,
5546            message: "missing current token".to_owned(),
5547        })?;
5548        let current_type = self.token_type_for_id(current);
5549        if matches(current_type) {
5550            self.consume();
5551            Ok(self.terminal_tree(current))
5552        } else {
5553            Err(AntlrError::MismatchedInput {
5554                expected: self.interval_display(intervals),
5555                found: self.vocabulary().display_name(current_type),
5556            })
5557        }
5558    }
5559
5560    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5561        let values = intervals
5562            .iter()
5563            .map(|(start, stop)| {
5564                if start == stop {
5565                    self.vocabulary().display_name(*start)
5566                } else {
5567                    format!(
5568                        "{}..{}",
5569                        self.vocabulary().display_name(*start),
5570                        self.vocabulary().display_name(*stop)
5571                    )
5572                }
5573            })
5574            .collect::<Vec<_>>()
5575            .join(", ");
5576        format!("{{{values}}}")
5577    }
5578
5579    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5580        if self.build_parse_trees {
5581            self.tree.finish_rule(context)
5582        } else {
5583            NodeId::placeholder()
5584        }
5585    }
5586
5587    /// Enters a generated parser rule and returns the context object the
5588    /// generated method should populate.
5589    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
5590        self.set_state(state);
5591        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
5592        self.rule_context_stack.push(RuleContextFrame {
5593            rule_index,
5594            invoking_state,
5595        });
5596        self.advance_rule_context_version();
5597        let start_index = self.current_visible_index();
5598        let mut context = ParserRuleContext::new(rule_index, invoking_state);
5599        if let Some(token) = self.token_id_at(start_index) {
5600            self.set_context_start(&mut context, token);
5601        }
5602        context
5603    }
5604
5605    /// Records the ATN source state for the next generated rule invocation.
5606    ///
5607    /// ANTLR's full-context prediction reconstructs caller follow states from
5608    /// each active rule context's invoking state. Generated Rust rule methods are
5609    /// plain functions, so the caller supplies that ATN state just before making a
5610    /// rule call; `enter_rule` consumes it when the callee starts.
5611    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
5612        let marker = self.pending_invoking_states.len();
5613        self.pending_invoking_states.push(invoking_state);
5614        marker
5615    }
5616
5617    /// Discards an invoking-state marker if the callee did not consume it.
5618    pub fn discard_invoking_state(&mut self, marker: usize) {
5619        self.pending_invoking_states.truncate(marker);
5620    }
5621
5622    /// Exits the current generated parser rule.
5623    pub fn exit_rule(&mut self) {
5624        self.rule_context_stack.pop();
5625        self.advance_rule_context_version();
5626    }
5627
5628    /// Returns caller follow states for interning in a parser ATN simulator's
5629    /// prediction store. States are yielded outermost to innermost.
5630    pub fn prediction_context_return_states<'a>(
5631        &'a self,
5632        atn: &'a Atn,
5633    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
5634        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
5635            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
5636                return None;
5637            };
5638            let Some(Transition::Rule { follow_state, .. }) = atn
5639                .state(state_number)
5640                .and_then(|state| state.transitions().first())
5641                .map(ParserTransition::data)
5642            else {
5643                return None;
5644            };
5645            Some(follow_state)
5646        })
5647    }
5648
5649    /// Returns a generation that changes whenever the active rule stack changes.
5650    ///
5651    /// A parser ATN simulator uses this to reuse an interned outer prediction
5652    /// context while generated predictions remain in the same rule context.
5653    pub const fn rule_context_version(&self) -> usize {
5654        self.rule_context_version
5655    }
5656
5657    const fn advance_rule_context_version(&mut self) {
5658        self.rule_context_version = self.rule_context_version.wrapping_add(1);
5659    }
5660
5661    /// Adds a generated parser child only when parse-tree construction is
5662    /// enabled. The match is recorded on the context either way (via `add_child`,
5663    /// or `note_matched_child` when trees are off) so generated recovery can tell
5664    /// whether the rule has matched anything yet without depending on `children`.
5665    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
5666        if self.build_parse_trees {
5667            self.tree.add_child(context, child);
5668        } else {
5669            context.note_matched_child();
5670        }
5671    }
5672
5673    fn release_tree_scratch_if_idle(&mut self) {
5674        if self.rule_context_stack.is_empty() {
5675            self.tree.release_scratch();
5676        }
5677    }
5678
5679    /// Finishes a generated parser rule and returns its parse-tree node.
5680    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
5681        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5682        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5683            self.set_context_stop(&mut context, token);
5684        }
5685        let node = self.rule_node(context);
5686        self.exit_rule();
5687        self.release_tree_scratch_if_idle();
5688        node
5689    }
5690
5691    /// Recovers a generated rule catch block after a committed mismatch.
5692    ///
5693    /// ANTLR's generated parsers catch recognition errors inside each rule,
5694    /// report the original error, then consume unexpected tokens until the
5695    /// caller's recovery set can resume. Tokens consumed during recovery become
5696    /// error nodes in the current rule context.
5697    pub fn recover_generated_rule(
5698        &mut self,
5699        context: &mut ParserRuleContext,
5700        atn: &Atn,
5701        error: AntlrError,
5702    ) {
5703        let diagnostic = self.generated_rule_error_diagnostic(error);
5704        self.push_generated_parser_diagnostic(diagnostic);
5705        self.generated_sync_expected = None;
5706        let recovery_symbols = self.context_expected_symbols(atn);
5707        loop {
5708            let symbol = self.la(1);
5709            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
5710                break;
5711            }
5712            let Some(token) = self.input.lt_id(1) else {
5713                break;
5714            };
5715            self.consume();
5716            let child = self.error_tree(token);
5717            self.add_parse_child(context, child);
5718        }
5719        self.record_syntax_errors(1);
5720    }
5721
5722    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
5723        if self
5724            .generated_parser_diagnostics
5725            .iter()
5726            .any(|existing| existing == &diagnostic)
5727        {
5728            return;
5729        }
5730        self.generated_parser_diagnostics.push(diagnostic);
5731    }
5732
5733    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
5734        match error {
5735            AntlrError::ParserError {
5736                line,
5737                column,
5738                message,
5739            } => ParserDiagnostic {
5740                line,
5741                column,
5742                message,
5743            },
5744            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
5745                self.input.lt(1),
5746                format!("mismatched input {found} expecting {expected}"),
5747            ),
5748            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
5749                self.input.lt(1),
5750                format!("no viable alternative at input {input}"),
5751            ),
5752            AntlrError::LexerError {
5753                line,
5754                column,
5755                message,
5756            } => ParserDiagnostic {
5757                line,
5758                column,
5759                message,
5760            },
5761            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
5762        }
5763    }
5764
5765    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
5766    pub fn finish_recursion_rule(
5767        &mut self,
5768        mut context: ParserRuleContext,
5769        consumed_eof: bool,
5770    ) -> ParseTree {
5771        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5772        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5773            self.set_context_stop(&mut context, token);
5774        }
5775        let node = self.rule_node(context);
5776        self.unroll_recursion_context();
5777        self.release_tree_scratch_if_idle();
5778        node
5779    }
5780
5781    /// Enters a generated left-recursive rule at `precedence`.
5782    pub fn enter_recursion_rule(
5783        &mut self,
5784        state: isize,
5785        rule_index: usize,
5786        precedence: i32,
5787    ) -> ParserRuleContext {
5788        self.precedence_stack.push(precedence);
5789        self.enter_rule(state, rule_index)
5790    }
5791
5792    /// Replaces the current context while expanding a left-recursive rule.
5793    pub fn push_new_recursion_context(
5794        &mut self,
5795        state: isize,
5796        rule_index: usize,
5797    ) -> ParserRuleContext {
5798        self.set_state(state);
5799        ParserRuleContext::new(rule_index, state)
5800    }
5801
5802    /// Wraps the previous left-recursive context before parsing the next
5803    /// recursive operator alternative.
5804    pub fn push_new_recursion_context_with_previous(
5805        &mut self,
5806        state: isize,
5807        rule_index: usize,
5808        current: &mut ParserRuleContext,
5809    ) {
5810        self.set_state(state);
5811        if let Some(stop) = self
5812            .rule_stop_token_index(self.input.index(), false)
5813            .and_then(|index| self.token_id_at(index))
5814        {
5815            self.set_context_stop(current, stop);
5816        }
5817        let invoking_state = current.invoking_state();
5818        let start = current.start_id();
5819        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
5820        if start.is_some() {
5821            replacement.set_start_from_context(current);
5822        }
5823        let previous = std::mem::replace(current, replacement);
5824        if self.build_parse_trees {
5825            let previous = self.rule_node(previous);
5826            self.tree.add_child(current, previous);
5827        }
5828    }
5829
5830    /// Leaves a generated left-recursive rule.
5831    pub fn unroll_recursion_context(&mut self) {
5832        if self.precedence_stack.len() > 1 {
5833            self.precedence_stack.pop();
5834        }
5835        self.exit_rule();
5836    }
5837
5838    /// Predicts a generated left-recursive loop from one-token lookahead.
5839    ///
5840    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
5841    /// `None` means caller overlap, a dangerous multi-token prefix, or an
5842    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
5843    /// prediction (which includes the exit alt and precedence filtering).
5844    ///
5845    /// Single-token operators and multi-token prefixes that do not shadow a
5846    /// lower-precedence single-token operator keep the one-token enter fast path.
5847    ///
5848    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
5849    /// operator must not force enter; the adaptive decision may need to select
5850    /// the loop exit instead.
5851    pub fn left_recursive_loop_enter_prediction(
5852        &mut self,
5853        atn: &Atn,
5854        state_number: usize,
5855        precedence: i32,
5856    ) -> Option<bool> {
5857        let symbol = self.la(1);
5858        if symbol == TOKEN_EOF {
5859            return Some(false);
5860        }
5861        let operator_lookahead =
5862            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
5863        let can_single = operator_lookahead.single_token.contains(symbol);
5864        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
5865        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
5866        if !can_single && !can_multi && !can_predicate {
5867            return Some(false);
5868        }
5869        if can_predicate && !can_single {
5870            return None;
5871        }
5872        // Multi-token-only at this precedence, but the same symbol is a
5873        // single-token operator at precedence 0: defer so exit can win when the
5874        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
5875        if !can_single && can_multi && precedence > 0 {
5876            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
5877            if baseline.single_token.contains(symbol) {
5878                return None;
5879            }
5880        }
5881        let atn_key = SharedAtnCacheKey::for_atn(atn);
5882        let cached_overlap = self
5883            .left_recursive_caller_overlap_cache
5884            .iter()
5885            .flatten()
5886            .find(|entry| {
5887                entry.atn_key == atn_key
5888                    && entry.state_number == state_number
5889                    && entry.symbol == symbol
5890                    && entry.context_version == self.rule_context_version
5891            })
5892            .map(|entry| entry.overlaps);
5893        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
5894            let overlaps = caller_context_can_match_symbol_before_state(
5895                atn,
5896                self.prediction_context_return_states(atn),
5897                state_number,
5898                symbol,
5899            );
5900            if let Some(slot) = self
5901                .left_recursive_caller_overlap_cache
5902                .iter_mut()
5903                .find(|slot| slot.is_none())
5904            {
5905                *slot = Some(LeftRecursiveCallerOverlap {
5906                    atn_key,
5907                    state_number,
5908                    symbol,
5909                    context_version: self.rule_context_version,
5910                    overlaps,
5911                });
5912            }
5913            overlaps
5914        });
5915        if caller_overlaps {
5916            return None;
5917        }
5918        Some(true)
5919    }
5920
5921    fn cached_left_recursive_operator_lookahead(
5922        atn: &Atn,
5923        state_number: usize,
5924        precedence: i32,
5925    ) -> Rc<LeftRecursiveOperatorLookahead> {
5926        with_shared_atn_caches(atn, |cache| {
5927            let key = (state_number, precedence);
5928            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
5929                return Rc::clone(cached);
5930            }
5931            let lookahead = Rc::new(left_recursive_operator_lookahead(
5932                atn,
5933                state_number,
5934                precedence,
5935            ));
5936            cache
5937                .left_recursive_operator_lookahead
5938                .insert(key, Rc::clone(&lookahead));
5939            lookahead
5940        })
5941    }
5942
5943    /// Checks whether a generated left-recursive loop can unambiguously enter
5944    /// its operator alternative from one-token lookahead.
5945    pub fn left_recursive_loop_enter_matches(
5946        &mut self,
5947        atn: &Atn,
5948        state_number: usize,
5949        precedence: i32,
5950    ) -> bool {
5951        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
5952    }
5953
5954    /// Implements generated `precpred(_ctx, k)` checks.
5955    pub fn precpred(&self, precedence: i32) -> bool {
5956        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
5957    }
5958
5959    /// Evaluates a generated parser semantic predicate at the current input
5960    /// position.
5961    pub fn parser_semantic_predicate_matches(
5962        &mut self,
5963        predicates: &[(usize, usize, ParserPredicate)],
5964        rule_index: usize,
5965        pred_index: usize,
5966    ) -> bool {
5967        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
5968    }
5969
5970    /// Evaluates a generated parser semantic predicate with the current integer
5971    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
5972    pub fn parser_semantic_predicate_matches_with_local(
5973        &mut self,
5974        predicates: &[(usize, usize, ParserPredicate)],
5975        rule_index: usize,
5976        pred_index: usize,
5977        local_int_arg: i32,
5978    ) -> bool {
5979        self.parser_semantic_predicate_matches_inner(
5980            predicates,
5981            rule_index,
5982            pred_index,
5983            Some((rule_index, i64::from(local_int_arg))),
5984        )
5985    }
5986
5987    fn parser_semantic_predicate_matches_inner(
5988        &mut self,
5989        predicates: &[(usize, usize, ParserPredicate)],
5990        rule_index: usize,
5991        pred_index: usize,
5992        local_int_arg: Option<(usize, i64)>,
5993    ) -> bool {
5994        let index = self.input.index();
5995        let member_values = self.int_members.clone();
5996        self.parser_predicate_matches(PredicateEval {
5997            index,
5998            rule_index,
5999            pred_index,
6000            predicates,
6001            semantics: None,
6002            context: None,
6003            local_int_arg,
6004            member_values: &member_values,
6005        })
6006    }
6007
6008    /// Evaluates a generated parser semantic predicate with access to the
6009    /// current generated rule context.
6010    pub fn parser_semantic_predicate_matches_with_context_and_local(
6011        &mut self,
6012        predicates: &[(usize, usize, ParserPredicate)],
6013        rule_index: usize,
6014        pred_index: usize,
6015        context: &ParserRuleContext,
6016        local_int_arg: i32,
6017    ) -> bool {
6018        let index = self.input.index();
6019        let member_values = self.int_members.clone();
6020        self.parser_predicate_matches(PredicateEval {
6021            index,
6022            rule_index,
6023            pred_index,
6024            predicates,
6025            semantics: None,
6026            context: Some(context),
6027            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6028            member_values: &member_values,
6029        })
6030    }
6031
6032    /// Evaluates a generated `SemIR` parser predicate with access to the current
6033    /// generated rule context.
6034    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
6035        &mut self,
6036        semantics: &ParserSemantics,
6037        rule_index: usize,
6038        pred_index: usize,
6039        context: &ParserRuleContext,
6040        local_int_arg: i32,
6041    ) -> bool {
6042        let index = self.input.index();
6043        let member_values = self.int_members.clone();
6044        self.parser_predicate_matches(PredicateEval {
6045            index,
6046            rule_index,
6047            pred_index,
6048            predicates: &[],
6049            semantics: Some(semantics),
6050            context: Some(context),
6051            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6052            member_values: &member_values,
6053        })
6054    }
6055
6056    /// Returns a generated fail-option message for a parser semantic
6057    /// predicate coordinate.
6058    pub fn parser_semantic_predicate_failure_message(
6059        &self,
6060        rule_index: usize,
6061        pred_index: usize,
6062        predicates: &[(usize, usize, ParserPredicate)],
6063    ) -> Option<&'static str> {
6064        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
6065    }
6066
6067    /// Matches any non-EOF token.
6068    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
6069        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6070            line: 0,
6071            column: 0,
6072            message: "missing current token".to_owned(),
6073        })?;
6074        if self.token_type_for_id(current) == TOKEN_EOF {
6075            return Err(AntlrError::MismatchedInput {
6076                expected: "wildcard".to_owned(),
6077                found: self.vocabulary().display_name(TOKEN_EOF),
6078            });
6079        }
6080        self.consume();
6081        Ok(self.terminal_tree(current))
6082    }
6083
6084    /// Generated parser synchronization hook. The current interpreter owns
6085    /// recovery; direct generated methods can call this as a no-op until the
6086    /// generated recovery strategy is expanded.
6087    #[allow(clippy::unnecessary_wraps)]
6088    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
6089        self.set_state(state);
6090        Ok(())
6091    }
6092
6093    /// Synchronizes a generated parser decision against the ATN lookahead set.
6094    ///
6095    /// ANTLR generated parsers call the error strategy before optional and loop
6096    /// decisions. When the current token cannot start any alternative, follow a
6097    /// nullable exit, or be deleted before a later synchronization token, the
6098    /// generated Rust method reports that decision-level mismatch instead of
6099    /// descending into a child rule that cannot start at the current token.
6100    pub fn sync_decision(
6101        &mut self,
6102        atn: &Atn,
6103        state_number: usize,
6104        current_context_empty: bool,
6105        loop_back: bool,
6106    ) -> Result<Vec<ParseTree>, AntlrError> {
6107        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
6108        self.generated_sync_expected = None;
6109        let Some(state) = atn.state(state_number) else {
6110            return Ok(Vec::new());
6111        };
6112        let Some(rule_index) = state.rule_index() else {
6113            return Ok(Vec::new());
6114        };
6115        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
6116            return Ok(Vec::new());
6117        };
6118        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6119        let symbol = self.la(1);
6120        let mut has_expected_symbols = false;
6121        let mut nullable = false;
6122        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
6123        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
6124        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
6125        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
6126        // and worth deleting; an implicit-follow EOF means the loop should simply
6127        // exit and leave the token for the (absent) caller — matching ANTLR, which
6128        // exits the loop via prediction rather than consuming up to a synthetic EOF.
6129        let mut explicit_eof_expected = false;
6130        for transition in &entry.transitions {
6131            if transition.symbols.contains(symbol) {
6132                return Ok(Vec::new());
6133            }
6134            has_expected_symbols |= !transition.symbols.is_empty();
6135            nullable |= transition.nullable;
6136            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
6137        }
6138        // Happy path: a nullable decision exits when the symbol is in the
6139        // rule-stack follow set. Answer the membership question with an
6140        // early-exit walk; the full union below is only needed for the
6141        // mismatch/deletion diagnostics.
6142        if nullable && self.context_expected_contains(atn, symbol) {
6143            return Ok(Vec::new());
6144        }
6145        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
6146        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
6147            return Ok(Vec::new());
6148        }
6149        let mut expected = TokenBitSet::default();
6150        for transition in &entry.transitions {
6151            expected.extend_from(&transition.symbols);
6152        }
6153        if let Some(context_expected) = context_expected {
6154            expected.extend_from(&context_expected);
6155        }
6156        let can_delete_in_place =
6157            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
6158        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
6159        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
6160        // least one iteration) does `consumeUntil` the follow set — multi-token
6161        // deletion, one error per skipped token across iterations; a loop ENTRY
6162        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
6163        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
6164        // unexpected token only when LA(2) is expected, otherwise report a mismatch
6165        // and leave recovery to the rule.
6166        //
6167        // The generated loop always presents the loop-ENTRY state to this method on
6168        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
6169        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
6170        // an iteration has been taken, and true on a `+` loop's first sync since its
6171        // mandatory first element is iteration 1). Treating a loop entry as a
6172        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
6173        // `c`s, which ANTLR rejects with `mismatched input`).
6174        let loop_sync = loop_back;
6175        if symbol != TOKEN_EOF && can_delete_in_place {
6176            let mut cursor = self.input.index();
6177            let mut skipped = Vec::new();
6178            loop {
6179                let current = self.token_type_at(cursor);
6180                if current == TOKEN_EOF {
6181                    break;
6182                }
6183                skipped.push(cursor);
6184                let next = self.consume_index(cursor, current);
6185                if next == cursor {
6186                    break;
6187                }
6188                let next_symbol = self.token_type_at(next);
6189                // Stop (and delete the skipped tokens as error nodes) when the next
6190                // token is a real expected continuation. EOF counts only when it is
6191                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
6192                // genuinely extraneous and the generated EOF match consumes the real
6193                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
6194                // rule-follow) does NOT count — the loop must exit and leave the
6195                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
6196                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
6197                    explicit_eof_expected
6198                } else {
6199                    expected.contains(next_symbol)
6200                };
6201                if next_is_expected_stop {
6202                    let current_token = self.input.lt(1);
6203                    let expected_symbols = expected.to_btree_set();
6204                    let message = format!(
6205                        "extraneous input {} expecting {}",
6206                        current_token
6207                            .as_ref()
6208                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6209                        self.expected_symbols_display(&expected_symbols)
6210                    );
6211                    self.push_generated_parser_diagnostic(diagnostic_for_token(
6212                        current_token,
6213                        message,
6214                    ));
6215                    self.record_syntax_errors(1);
6216                    let mut children = Vec::with_capacity(skipped.len());
6217                    for index in skipped {
6218                        if let Some(token) = self.token_id_at(index) {
6219                            self.consume();
6220                            children.push(self.error_tree(token));
6221                        }
6222                    }
6223                    return Ok(children);
6224                }
6225                // A non-loop block entry deletes at most one token (single-token
6226                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6227                // is reported at the first token instead of skipping ahead.
6228                if !loop_sync {
6229                    break;
6230                }
6231                cursor = next;
6232            }
6233        }
6234        if nullable {
6235            self.generated_sync_expected = Some(expected);
6236            return Ok(Vec::new());
6237        }
6238        let current = self.input.lt(1);
6239        let expected_symbols = expected.to_btree_set();
6240        Err(AntlrError::ParserError {
6241            line: current.as_ref().map(Token::line).unwrap_or_default(),
6242            column: current.as_ref().map(Token::column).unwrap_or_default(),
6243            message: format!(
6244                "mismatched input {} expecting {}",
6245                current
6246                    .as_ref()
6247                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6248                self.expected_symbols_display(&expected_symbols)
6249            ),
6250        })
6251    }
6252
6253    /// Returns a generated-parser prediction when one token of lookahead
6254    /// uniquely selects an alternative for `state_number`.
6255    ///
6256    /// This mirrors the interpreter's LL(1) commit point and lets generated
6257    /// recursive-descent methods avoid invoking the adaptive simulator for
6258    /// simple optional/block/loop decisions.
6259    pub fn ll1_decision_prediction(
6260        &mut self,
6261        atn: &Atn,
6262        state_number: usize,
6263    ) -> Option<ParserAtnPrediction> {
6264        let state = atn.state(state_number)?;
6265        if state.precedence_rule_decision() {
6266            return None;
6267        }
6268        let rule_stop = state
6269            .rule_index()
6270            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6271        let symbol = self.la(1);
6272        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6273        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6274            alt: alt + 1,
6275            requires_full_context: false,
6276            has_semantic_context: false,
6277            diagnostic: None,
6278        })
6279    }
6280
6281    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6282        let mut expected = BTreeSet::new();
6283        for index in (1..self.rule_context_stack.len()).rev() {
6284            let invoking_state = self.rule_context_stack[index].invoking_state;
6285            let Ok(state_number) = usize::try_from(invoking_state) else {
6286                continue;
6287            };
6288            let Some(Transition::Rule { follow_state, .. }) = atn
6289                .state(state_number)
6290                .and_then(|state| state.transitions().first())
6291                .map(ParserTransition::data)
6292            else {
6293                continue;
6294            };
6295            let return_state = follow_state;
6296            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6297            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
6298                return expected;
6299            }
6300        }
6301        expected.insert(TOKEN_EOF);
6302        expected
6303    }
6304
6305    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6306        let mut expected = TokenBitSet::default();
6307        for index in (1..self.rule_context_stack.len()).rev() {
6308            let invoking_state = self.rule_context_stack[index].invoking_state;
6309            let Ok(state_number) = usize::try_from(invoking_state) else {
6310                continue;
6311            };
6312            let Some(Transition::Rule { follow_state, .. }) = atn
6313                .state(state_number)
6314                .and_then(|state| state.transitions().first())
6315                .map(ParserTransition::data)
6316            else {
6317                continue;
6318            };
6319            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
6320            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6321                return expected;
6322            }
6323        }
6324        expected.insert(TOKEN_EOF);
6325        expected
6326    }
6327
6328    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
6329    /// without materializing the union.
6330    ///
6331    /// This walks the rule-invocation stack directly, innermost frame first —
6332    /// the same frames, in the same order, with the same rule-stop gating as
6333    /// the same outer-context return-state chain used by adaptive prediction.
6334    /// The nullable
6335    /// exit in `sync_decision` asks only this membership question, and on
6336    /// valid input the innermost frame answers it, so the early exit replaces
6337    /// an O(stack-depth) set union per loop/optional exit with one probe.
6338    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
6339        for index in (1..self.rule_context_stack.len()).rev() {
6340            let invoking_state = self.rule_context_stack[index].invoking_state;
6341            let Ok(state_number) = usize::try_from(invoking_state) else {
6342                continue;
6343            };
6344            let Some(Transition::Rule { follow_state, .. }) = atn
6345                .state(state_number)
6346                .and_then(|state| state.transitions().first())
6347                .map(ParserTransition::data)
6348            else {
6349                continue;
6350            };
6351            if self
6352                .cached_state_expected_token_set(atn, follow_state)
6353                .contains(symbol)
6354            {
6355                return true;
6356            }
6357            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6358                return false;
6359            }
6360        }
6361        symbol == TOKEN_EOF
6362    }
6363
6364    /// Builds a generated no-viable-alternative parser error.
6365    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
6366        let error_index = self.input.index();
6367        self.no_viable_alternative_error_at(start_index, error_index)
6368    }
6369
6370    /// Builds a generated no-viable-alternative parser error at the simulator's
6371    /// failing lookahead index. `adaptive_predict` restores the input cursor
6372    /// before returning, so generated parsers have to pass the recorded index
6373    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
6374    pub fn no_viable_alternative_error_at(
6375        &self,
6376        start_index: usize,
6377        error_index: usize,
6378    ) -> AntlrError {
6379        let diagnostic = self.no_viable_alternative(start_index, error_index);
6380        AntlrError::ParserError {
6381            line: diagnostic.line,
6382            column: diagnostic.column,
6383            message: diagnostic.message,
6384        }
6385    }
6386
6387    /// Builds a generated failed-predicate parser error.
6388    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
6389        let current = self.input.lt(1);
6390        AntlrError::ParserError {
6391            line: current.as_ref().map(Token::line).unwrap_or_default(),
6392            column: current.as_ref().map(Token::column).unwrap_or_default(),
6393            message: format!("rule failed predicate: {}", message.into()),
6394        }
6395    }
6396
6397    /// Builds a generated parser error for a semantic predicate with ANTLR's
6398    /// `<fail='...'>` option.
6399    pub fn failed_predicate_option_error(
6400        &self,
6401        rule_index: usize,
6402        message: impl Into<String>,
6403    ) -> AntlrError {
6404        let current = self.input.lt(1);
6405        let rule_name = self
6406            .rule_names()
6407            .get(rule_index)
6408            .map_or_else(|| rule_index.to_string(), Clone::clone);
6409        AntlrError::ParserError {
6410            line: current.as_ref().map(Token::line).unwrap_or_default(),
6411            column: current.as_ref().map(Token::column).unwrap_or_default(),
6412            message: format!("rule {rule_name} {}", message.into()),
6413        }
6414    }
6415
6416    /// Builds a generated parser-action event at the current input position.
6417    pub fn parser_action_at_current(
6418        &mut self,
6419        source_state: usize,
6420        rule_index: usize,
6421        start_index: usize,
6422        consumed_eof: bool,
6423    ) -> ParserAction {
6424        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6425        ParserAction::new(source_state, rule_index, start_index, stop_index)
6426    }
6427
6428    /// Offers a committed parser action event to the user semantic hook.
6429    ///
6430    /// Generated parsers call this for action source states that were present
6431    /// in the ATN but not translated into a built-in Rust action template.
6432    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
6433        let rule_index = action.rule_index();
6434        let rule_name = self.rule_names().get(rule_index).cloned();
6435        let context = None;
6436        let input = &mut self.input;
6437        let semantic_hooks = &mut self.semantic_hooks;
6438        let member_values = &self.int_members;
6439        let mut ctx = ParserSemCtx {
6440            input,
6441            tree_storage: &self.tree,
6442            rule_index,
6443            coordinate_index: usize::MAX,
6444            rule_name,
6445            context,
6446            tree: Some(tree),
6447            local_int_arg: None,
6448            member_values,
6449            action: Some(action),
6450        };
6451        let handled = semantic_hooks.action(&mut ctx, action);
6452        // This action reached the hook because it had no translated arm. If no
6453        // hook handled it either (`SemanticHooks::action` returns `false`), the
6454        // committed action is silently dropped — record it so the parse entry
6455        // can fail loud under the fail-loud boundary, mirroring unknown
6456        // predicates. `assume-*` policies opt out of the fail-loud recording.
6457        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
6458            let coordinate = (rule_index, action.source_state());
6459            if !self.unhandled_action_hits.contains(&coordinate) {
6460                self.unhandled_action_hits.push(coordinate);
6461            }
6462        }
6463        handled
6464    }
6465
6466    /// Attempts to execute a whole generated rule by committing simulator
6467    /// decisions directly. Unsupported constructs or decisions that need
6468    /// full-context / predicate evaluation restore the input cursor and fall
6469    /// back to [`Self::parse_atn_rule`].
6470    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
6471        &mut self,
6472        atn: &'atn Atn,
6473        simulator: &mut ParserAtnSimulator<'atn>,
6474        rule_index: usize,
6475    ) -> Result<ParseTree, AntlrError> {
6476        let start_index = self.current_visible_index();
6477        self.clear_prediction_diagnostics();
6478        self.reset_per_parse_caches();
6479        self.reset_recognition_arena();
6480        let tree_checkpoint = self.tree.checkpoint();
6481        let mut decision_by_state = vec![None; atn.states().len()];
6482        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
6483            if let Some(slot) = decision_by_state.get_mut(state_number) {
6484                *slot = Some(decision);
6485            }
6486        }
6487
6488        let result = DirectAdaptiveParser {
6489            parser: self,
6490            atn,
6491            simulator,
6492            decision_by_state,
6493            steps: 0,
6494        }
6495        .parse_rule(rule_index, -1, 0);
6496
6497        match result {
6498            Ok(tree) => {
6499                self.report_token_source_errors();
6500                self.release_tree_scratch_if_idle();
6501                Ok(tree)
6502            }
6503            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
6504                let _ = reason;
6505                self.tree.rollback(tree_checkpoint);
6506                self.input.seek(start_index);
6507                self.parse_atn_rule(atn, rule_index)
6508            }
6509        }
6510    }
6511
6512    /// Parses a generated rule by interpreting the parser ATN from the rule's
6513    /// start state to its stop state.
6514    ///
6515    /// The recognizer backtracks across alternatives and loop exits using token
6516    /// stream indices instead of committing to input consumption immediately.
6517    /// Once a viable ATN path is found, the parser commits the accepted token
6518    /// interval and returns a rule node whose children mirror every grammar
6519    /// rule invocation reached on that path, matching ANTLR's parse-tree
6520    /// shape.
6521    pub fn parse_atn_rule(
6522        &mut self,
6523        atn: &Atn,
6524        rule_index: usize,
6525    ) -> Result<ParseTree, AntlrError> {
6526        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
6527    }
6528
6529    /// Parses a generated rule by interpreting the parser ATN with an initial
6530    /// left-recursive precedence threshold.
6531    pub fn parse_atn_rule_with_precedence(
6532        &mut self,
6533        atn: &Atn,
6534        rule_index: usize,
6535        precedence: i32,
6536    ) -> Result<ParseTree, AntlrError> {
6537        self.parse_atn_rule_with_precedence_inner(atn, rule_index, precedence, None)
6538    }
6539
6540    fn parse_atn_rule_with_precedence_inner(
6541        &mut self,
6542        atn: &Atn,
6543        rule_index: usize,
6544        precedence: i32,
6545        predicate_context: Option<FastPredicateContext<'_>>,
6546    ) -> Result<ParseTree, AntlrError> {
6547        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6548            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6549        })?;
6550        let stop_state = atn
6551            .rule_to_stop_state()
6552            .get(rule_index)
6553            .filter(|state| *state != usize::MAX)
6554            .ok_or_else(|| {
6555                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6556            })?;
6557
6558        let start_index = self.current_visible_index();
6559        self.clear_prediction_diagnostics();
6560        self.reset_per_parse_caches();
6561        self.reset_recognition_arena();
6562        let caller_follow_state = self.pending_invoking_follow_state(atn);
6563        self.fast_recovery_enabled = false;
6564        self.fast_token_nodes_enabled = false;
6565        let top_request = FastRecognizeTopRequest {
6566            start_state,
6567            stop_state,
6568            start_index,
6569            precedence,
6570            caller_follow_state,
6571        };
6572        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
6573        self.fast_token_nodes_enabled = self.build_parse_trees;
6574        let needs_tree_retry = matches!(
6575            &first_pass,
6576            Ok((outcome, _))
6577                if self.build_parse_trees
6578                    && self
6579                        .recognition_arena
6580                        .sequence_has_left_recursive_boundary(outcome.nodes)
6581        );
6582        let needs_retry = match &first_pass {
6583            // The FIRST-set prefilter trims speculative rule calls that can't
6584            // match the current lookahead — useful for perf on grammars with
6585            // many epsilon-reachable rules, but the trim also bypasses
6586            // single-token insertion / deletion recovery that ANTLR's
6587            // reference parser runs at the child rule's first consuming
6588            // transition. Retry without the prefilter whenever the first pass
6589            // either produced no outcome at all or produced a recovered
6590            // outcome (diagnostics non-empty), since the second pass might
6591            // surface a child-level recovery with cleaner diagnostics or
6592            // closer parity to ANTLR's tree shape. Left-recursive tree
6593            // boundaries also need the token-node pass; otherwise the fold has
6594            // no concrete left operand to wrap into ANTLR's recursive context.
6595            Err(_) => true,
6596            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
6597        };
6598        let (outcome, _expected) = if needs_retry {
6599            self.fast_first_set_prefilter = false;
6600            self.fast_recovery_enabled = false;
6601            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6602            let clean_selected = if needs_tree_retry {
6603                match clean_retry {
6604                    ok @ Ok(_) => ok,
6605                    Err(_) => first_pass,
6606                }
6607            } else {
6608                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
6609            };
6610            let selected = if clean_selected.is_err()
6611                || matches!(&clean_selected, Ok((outcome, _)) if !outcome.diagnostics.is_empty())
6612            {
6613                self.fast_recovery_enabled = true;
6614                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6615                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
6616            } else {
6617                clean_selected
6618            };
6619            self.fast_first_set_prefilter = true;
6620            self.fast_recovery_enabled = true;
6621            selected.map_err(|expected| {
6622                if predicate_context.is_some()
6623                    && let Some(error) = self.unknown_semantic_error()
6624                {
6625                    self.report_token_source_errors();
6626                    return error;
6627                }
6628                let error = self.recognition_error(rule_index, start_index, &expected);
6629                self.record_syntax_errors(1);
6630                self.report_token_source_errors();
6631                error
6632            })?
6633        } else {
6634            first_pass.expect("first_pass is Ok in the no-retry branch")
6635        };
6636        if predicate_context.is_some()
6637            && let Some(error) = self.unknown_semantic_error()
6638        {
6639            self.report_token_source_errors();
6640            return Err(error);
6641        }
6642        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6643        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
6644        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6645        self.report_token_source_errors();
6646        let mut context = ParserRuleContext::with_child_capacity(
6647            rule_index,
6648            self.state(),
6649            if self.build_parse_trees {
6650                self.recognition_arena.sequence_len(outcome.nodes)
6651            } else {
6652                0
6653            },
6654        );
6655        if let Some(token) = self.token_id_at(start_index) {
6656            self.set_context_start(&mut context, token);
6657        }
6658        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
6659        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
6660            self.set_context_stop(&mut context, token);
6661        }
6662        let live_root = if self.build_parse_trees {
6663            self.recognition_arena
6664                .fold_left_recursive_boundaries(outcome.nodes)
6665        } else {
6666            outcome.nodes
6667        };
6668        if self.build_parse_trees {
6669            if self
6670                .recognition_arena
6671                .sequence_has_explicit_token(live_root)
6672            {
6673                let mut cursor = live_root;
6674                while let Some(link) = self.recognition_arena.link(cursor) {
6675                    let child = self.arena_recognized_node_tree(link.head, false)?;
6676                    self.tree.add_child(&mut context, child);
6677                    cursor = link.tail;
6678                }
6679            } else {
6680                self.add_arena_implicit_token_children(
6681                    &mut context,
6682                    start_index,
6683                    stop_index,
6684                    live_root,
6685                )?;
6686            }
6687        }
6688        self.finish_recognition_arena(live_root, outcome.diagnostics);
6689        self.input.seek(outcome.index);
6690
6691        let tree = self.rule_node(context);
6692        self.release_tree_scratch_if_idle();
6693        Ok(tree)
6694    }
6695
6696    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
6697        let invoking_state = self.pending_invoking_states.last().copied()?;
6698        let state_number = usize::try_from(invoking_state).ok()?;
6699        match atn.state(state_number)?.transitions().first()?.data() {
6700            Transition::Rule { follow_state, .. } => Some(follow_state),
6701            _ => None,
6702        }
6703    }
6704
6705    #[cfg(test)]
6706    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
6707        caller_follow_token_info_for_stream(&mut self.input, index)
6708    }
6709
6710    /// Runs the fast recognizer once from the rule's start state and returns
6711    /// the best outcome or the per-attempt expected-token accumulator. The
6712    /// caller flips `fast_first_set_prefilter` between calls when a retry is
6713    /// needed, so the FIRST-set cache is left intact across both passes.
6714    fn fast_recognize_top(
6715        &mut self,
6716        atn: &Atn,
6717        request: FastRecognizeTopRequest,
6718        predicate_context: Option<FastPredicateContext<'_>>,
6719    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
6720        let FastRecognizeTopRequest {
6721            start_state,
6722            stop_state,
6723            start_index,
6724            precedence,
6725            caller_follow_state,
6726        } = request;
6727        // `input.size()` is intentionally only the currently buffered token
6728        // count here. Do not restore an up-front fill just to size this map:
6729        // a small floor avoids tiny-input churn, and larger inputs reserve from
6730        // the buffered token count without forcing startup tokenization. The
6731        // 8x multiplier matches the empirical
6732        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
6733        // Kotlin ~12× memo entries per token), so the table avoids one
6734        // rehash on the typical hot path.
6735        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
6736        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
6737        recognize_scratch.prepare(memo_capacity);
6738        let mut expected = ExpectedTokens::default();
6739        let empty_recovery = self.empty_recovery_symbols();
6740        let outcomes = self.recognize_state_fast(
6741            atn,
6742            FastRecognizeRequest {
6743                state_number: start_state,
6744                stop_state,
6745                index: start_index,
6746                rule_start_index: start_index,
6747                decision_start_index: None,
6748                precedence,
6749                depth: 0,
6750                recovery_symbols: empty_recovery,
6751                recovery_state: None,
6752            },
6753            FastRecognizeScratch {
6754                predicate_context,
6755                visiting: &mut recognize_scratch.visiting,
6756                memo: &mut recognize_scratch.memo,
6757                expected: &mut expected,
6758                native_depth: 0,
6759            },
6760        );
6761        recognize_scratch.release_oversized_memo();
6762        self.fast_recognize_scratch = recognize_scratch;
6763        #[cfg(feature = "perf-counters")]
6764        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
6765            perf_counters::dump();
6766            perf_counters::reset();
6767        }
6768        let caller_follow =
6769            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
6770        let selected = {
6771            let arena = &self.recognition_arena;
6772            let input = &mut self.input;
6773            select_best_fast_outcome(
6774                outcomes.into_iter(),
6775                self.prediction_mode,
6776                caller_follow.as_deref(),
6777                |index| caller_follow_token_info_for_stream(input, index),
6778                arena,
6779            )
6780        };
6781        match selected {
6782            Some(mut outcome) => {
6783                if self.build_parse_trees {
6784                    self.materialize_fast_outcome_nodes(&mut outcome);
6785                }
6786                Ok((outcome, expected))
6787            }
6788            None => Err(expected),
6789        }
6790    }
6791
6792    /// Converts one speculative arena record into the flat public CST.
6793    fn arena_recognized_node_tree(
6794        &mut self,
6795        node_id: RecognizedNodeId,
6796        track_alt_numbers: bool,
6797    ) -> Result<ParseTree, AntlrError> {
6798        let node = self.recognition_arena.node(node_id);
6799        match node {
6800            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
6801            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
6802            ArenaRecognizedNode::MissingToken { extra } => {
6803                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
6804                    RecognitionExtra::MissingToken {
6805                        token_type,
6806                        at_index,
6807                        text,
6808                    } => (*token_type, *at_index as usize, text.clone()),
6809                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
6810                        unreachable!("missing-token node must reference missing-token extra")
6811                    }
6812                };
6813                let (line, column) = self
6814                    .token_at(at_index)
6815                    .map_or((0, 0), |token| (token.line(), token.column()));
6816                let token = self.insert_synthetic_token(token_type, text, line, column)?;
6817                Ok(self.error_tree(token))
6818            }
6819            ArenaRecognizedNode::Rule {
6820                rule_index,
6821                invoking_state,
6822                alt_number,
6823                start_index,
6824                stop_index,
6825                return_values,
6826                children,
6827            } => {
6828                let mut context = ParserRuleContext::with_child_capacity(
6829                    rule_index as usize,
6830                    invoking_state as isize,
6831                    self.recognition_arena.sequence_len(children),
6832                );
6833                if track_alt_numbers {
6834                    context.set_alt_number(alt_number as usize);
6835                }
6836                if let Some(extra) = return_values {
6837                    let RecognitionExtra::ReturnValues(values) =
6838                        self.recognition_arena.extra(extra)
6839                    else {
6840                        unreachable!("rule node must reference return-values extra");
6841                    };
6842                    for (name, value) in values {
6843                        context.set_int_return(name.clone(), *value);
6844                    }
6845                }
6846                if let Some(token) = self.token_id_at(start_index as usize) {
6847                    self.set_context_start(&mut context, token);
6848                }
6849                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6850                    self.set_context_stop(&mut context, token);
6851                }
6852                let mut cursor = self
6853                    .recognition_arena
6854                    .fold_left_recursive_boundaries(children);
6855                while let Some(link) = self.recognition_arena.link(cursor) {
6856                    let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
6857                    self.tree.add_child(&mut context, child);
6858                    cursor = link.tail;
6859                }
6860                Ok(self.rule_node(context))
6861            }
6862            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
6863                Err(AntlrError::Unsupported(format!(
6864                    "unfolded left-recursive boundary for rule {rule_index}"
6865                )))
6866            }
6867        }
6868    }
6869
6870    fn arena_recognized_node_tree_with_implicit_tokens(
6871        &mut self,
6872        node_id: RecognizedNodeId,
6873    ) -> Result<ParseTree, AntlrError> {
6874        let node = self.recognition_arena.node(node_id);
6875        match node {
6876            ArenaRecognizedNode::Rule {
6877                rule_index,
6878                invoking_state,
6879                start_index,
6880                stop_index,
6881                children,
6882                ..
6883            } => {
6884                let mut context = ParserRuleContext::with_child_capacity(
6885                    rule_index as usize,
6886                    invoking_state as isize,
6887                    self.recognition_arena.sequence_len(children),
6888                );
6889                if let Some(token) = self.token_id_at(start_index as usize) {
6890                    self.set_context_start(&mut context, token);
6891                }
6892                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6893                    self.set_context_stop(&mut context, token);
6894                }
6895                let children = self
6896                    .recognition_arena
6897                    .fold_left_recursive_boundaries(children);
6898                self.add_arena_implicit_token_children(
6899                    &mut context,
6900                    start_index as usize,
6901                    stop_index.map(|index| index as usize),
6902                    children,
6903                )?;
6904                Ok(self.rule_node(context))
6905            }
6906            _ => self.arena_recognized_node_tree(node_id, false),
6907        }
6908    }
6909
6910    fn add_arena_implicit_token_children(
6911        &mut self,
6912        context: &mut ParserRuleContext,
6913        start_index: usize,
6914        stop_index: Option<usize>,
6915        mut children: NodeSeqId,
6916    ) -> Result<(), AntlrError> {
6917        let mut cursor = Some(start_index);
6918        while let Some(link) = self.recognition_arena.link(children) {
6919            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
6920                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
6921                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6922                self.tree.add_child(context, child);
6923                if let Some(child_stop) = child_stop {
6924                    cursor = self.next_visible_after_token(child_stop);
6925                }
6926            } else {
6927                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6928                self.tree.add_child(context, child);
6929            }
6930            children = link.tail;
6931        }
6932        if let Some(stop) = stop_index {
6933            self.add_visible_terminals_through(context, cursor, stop)?;
6934        }
6935        Ok(())
6936    }
6937
6938    fn add_visible_terminals_before(
6939        &mut self,
6940        context: &mut ParserRuleContext,
6941        cursor: &mut Option<usize>,
6942        before: usize,
6943    ) -> Result<(), AntlrError> {
6944        let Some(stop) = before.checked_sub(1) else {
6945            return Ok(());
6946        };
6947        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
6948        *cursor = next;
6949        Ok(())
6950    }
6951
6952    fn add_visible_terminals_through(
6953        &mut self,
6954        context: &mut ParserRuleContext,
6955        mut cursor: Option<usize>,
6956        stop: usize,
6957    ) -> Result<Option<usize>, AntlrError> {
6958        while let Some(index) = cursor {
6959            if index > stop {
6960                return Ok(Some(index));
6961            }
6962            let token = self
6963                .input
6964                .get_id(index)
6965                .ok_or_else(|| AntlrError::ParserError {
6966                    line: 0,
6967                    column: 0,
6968                    message: format!("missing token at index {index}"),
6969                })?;
6970            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
6971            let child = self.terminal_tree(token);
6972            self.tree.add_child(context, child);
6973            if is_eof {
6974                return Ok(None);
6975            }
6976            cursor = self.next_visible_after_token(index);
6977        }
6978        Ok(None)
6979    }
6980
6981    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
6982        let next = self.input.next_visible_after(index);
6983        (next != index).then_some(next)
6984    }
6985
6986    /// Parses a generated rule and returns semantic actions reached on the
6987    /// selected ATN path.
6988    ///
6989    /// This slower path preserves action ordering and token intervals for
6990    /// generated code that replays target-specific action templates after the
6991    /// recognizer has chosen one viable parse path.
6992    pub fn parse_atn_rule_with_actions(
6993        &mut self,
6994        atn: &Atn,
6995        rule_index: usize,
6996    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6997        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
6998    }
6999
7000    /// Parses a generated rule and emits ATN actions plus selected rule-init
7001    /// actions reached on the chosen path.
7002    ///
7003    /// Generated parsers use this when a grammar contains rule-level `@init`
7004    /// templates that must run for nested rule invocations. The runtime keeps
7005    /// the action list path-sensitive, so init templates are replayed only for
7006    /// rules that were actually entered by the selected parse.
7007    pub fn parse_atn_rule_with_action_inits(
7008        &mut self,
7009        atn: &Atn,
7010        rule_index: usize,
7011        init_action_rules: &[usize],
7012    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7013        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7014    }
7015
7016    /// Parses a generated rule with optional semantic-action replay features.
7017    ///
7018    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7019    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7020    /// unchanged for grammars that do not request that target template.
7021    pub fn parse_atn_rule_with_action_options(
7022        &mut self,
7023        atn: &Atn,
7024        rule_index: usize,
7025        init_action_rules: &[usize],
7026        track_alt_numbers: bool,
7027    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7028        self.parse_atn_rule_with_runtime_options(
7029            atn,
7030            rule_index,
7031            ParserRuntimeOptions {
7032                init_action_rules,
7033                track_alt_numbers,
7034                ..ParserRuntimeOptions::default()
7035            },
7036        )
7037    }
7038
7039    /// Parses a generated rule with action replay and parser predicate support.
7040    ///
7041    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7042    /// target-template predicate semantics emitted by the generator. Missing
7043    /// entries are treated as true so unsupported predicate-free grammars keep
7044    /// the previous unconditional transition behavior.
7045    pub fn parse_atn_rule_with_runtime_options(
7046        &mut self,
7047        atn: &Atn,
7048        rule_index: usize,
7049        options: ParserRuntimeOptions<'_>,
7050    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7051        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7052    }
7053
7054    /// Parses a generated rule with action replay, parser predicate support,
7055    /// and an initial left-recursive precedence threshold.
7056    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7057        &mut self,
7058        atn: &Atn,
7059        rule_index: usize,
7060        precedence: i32,
7061        options: ParserRuntimeOptions<'_>,
7062    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7063        let ParserRuntimeOptions {
7064            init_action_rules,
7065            track_alt_numbers,
7066            predicates,
7067            semantics,
7068            rule_args,
7069            member_actions,
7070            return_actions,
7071            unknown_predicate_policy,
7072        } = options;
7073        if init_action_rules.is_empty()
7074            && !track_alt_numbers
7075            && predicates.is_empty()
7076            && semantics.is_none()
7077            && rule_args.is_empty()
7078            && member_actions.is_empty()
7079            && return_actions.is_empty()
7080            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7081            && !atn_has_observable_action_transitions(atn)
7082            && (!self.semantic_hooks.observes_parser_predicates()
7083                || !atn_has_predicate_transitions(atn))
7084        {
7085            return self
7086                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7087                .map(|tree| (tree, Vec::new()));
7088        }
7089        if can_use_fast_predicate_recognizer(atn, &options) {
7090            self.unknown_predicate_policy = unknown_predicate_policy;
7091            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7092            let member_values = self.int_members.clone();
7093            let result = self
7094                .parse_atn_rule_with_precedence_inner(
7095                    atn,
7096                    rule_index,
7097                    precedence,
7098                    Some(FastPredicateContext {
7099                        predicates,
7100                        semantics,
7101                        member_values: &member_values,
7102                    }),
7103                )
7104                .map(|tree| (tree, Vec::new()));
7105            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7106                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7107            }
7108            return result;
7109        }
7110        self.unknown_predicate_policy = unknown_predicate_policy;
7111        // A generated parent may have already recorded unknown-predicate
7112        // coordinates before descending into this (interpreted) child. Clearing
7113        // unconditionally would drop them before the parent's public entry
7114        // surfaces them, so stash and restore around this call: recognition sees
7115        // only the hits it records itself (so the fail-loud check below reflects
7116        // this rule), and the parent's prior hits are merged back afterward.
7117        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7118        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7119            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7120        })?;
7121        let stop_state = atn
7122            .rule_to_stop_state()
7123            .get(rule_index)
7124            .filter(|state| *state != usize::MAX)
7125            .ok_or_else(|| {
7126                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7127            })?;
7128
7129        let start_index = self.current_visible_index();
7130        self.clear_prediction_diagnostics();
7131        self.reset_per_parse_caches();
7132        self.reset_recognition_arena();
7133        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7134        let invoking_state = self.pending_invoking_states.pop();
7135        let local_int_arg = invoking_state
7136            .and_then(|state| usize::try_from(state).ok())
7137            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7138        let mut visiting = BTreeSet::new();
7139        let mut memo = BTreeMap::new();
7140        let mut expected = ExpectedTokens::default();
7141        let member_values = self.int_members.clone();
7142        let return_values = BTreeMap::new();
7143        let outcomes = self.recognize_state(
7144            atn,
7145            RecognizeRequest {
7146                state_number: start_state,
7147                stop_state,
7148                index: start_index,
7149                rule_start_index: start_index,
7150                decision_start_index: None,
7151                init_action_rules: &init_action_rules,
7152                predicates,
7153                semantics,
7154                rule_args,
7155                member_actions,
7156                return_actions,
7157                local_int_arg,
7158                member_values,
7159                return_values,
7160                rule_alt_number: 0,
7161                track_alt_numbers,
7162                consumed_eof: false,
7163                precedence,
7164                depth: 0,
7165                recovery_symbols: BTreeSet::new(),
7166                recovery_state: None,
7167            },
7168            &mut visiting,
7169            &mut memo,
7170            &mut expected,
7171        );
7172        if let Some(error) = self.unknown_semantic_error() {
7173            self.report_token_source_errors();
7174            // Keep the recorded coordinates: when this interpreted rule is a
7175            // child of a generated parent, the parent's catch block recovers an
7176            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7177            // coordinate must survive on the parser for the top-level entry's
7178            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7179            // is handled by clearing at the top-level generated entry instead.
7180            return Err(error);
7181        }
7182        // Recognition recorded no unresolved coordinate of its own; merge the
7183        // parent's prior hits back so its public entry can still surface them.
7184        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7185        let Some(outcome) = select_best_outcome(
7186            outcomes.into_iter(),
7187            self.prediction_mode,
7188            &self.recognition_arena,
7189        ) else {
7190            let error = self.recognition_error(rule_index, start_index, &expected);
7191            self.record_syntax_errors(1);
7192            self.report_token_source_errors();
7193            return Err(error);
7194        };
7195
7196        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7197        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7198        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7199        self.report_token_source_errors();
7200        let mut actions = outcome.actions;
7201        if init_action_rules.contains(&rule_index) {
7202            actions.insert(
7203                0,
7204                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7205            );
7206        }
7207        let mut context =
7208            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7209        if track_alt_numbers {
7210            context.set_alt_number(outcome.alt_number);
7211        }
7212        for (name, value) in outcome.return_values {
7213            context.set_int_return(name, value);
7214        }
7215        if let Some(token) = self.token_id_at(start_index) {
7216            self.set_context_start(&mut context, token);
7217        }
7218        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7219            self.set_context_stop(&mut context, token);
7220        }
7221        let live_root = if self.build_parse_trees {
7222            self.recognition_arena
7223                .fold_left_recursive_boundaries(outcome.nodes)
7224        } else {
7225            outcome.nodes
7226        };
7227        if self.build_parse_trees {
7228            let mut nodes = live_root;
7229            while let Some(link) = self.recognition_arena.link(nodes) {
7230                let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
7231                self.tree.add_child(&mut context, child);
7232                nodes = link.tail;
7233            }
7234        }
7235        self.finish_recognition_arena(live_root, outcome.diagnostics);
7236        self.input.seek(outcome.index);
7237
7238        let tree = self.rule_node(context);
7239        self.release_tree_scratch_if_idle();
7240        Ok((tree, actions))
7241    }
7242
7243    /// Temporary parser entry used by generated parser methods while the parser
7244    /// ATN simulator is being implemented.
7245    ///
7246    /// This keeps generated parser crates buildable and gives us a stable method
7247    /// surface for every grammar rule. It intentionally accepts all remaining
7248    /// tokens into one rule context; it is not the final parser semantics.
7249    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
7250        let mut context = ParserRuleContext::new(rule_index, self.state());
7251        while self.la(1) != TOKEN_EOF {
7252            let token_type = self.la(1);
7253            let child = self.match_token(token_type)?;
7254            if self.build_parse_trees {
7255                self.tree.add_child(&mut context, child);
7256            }
7257        }
7258        if self.build_parse_trees {
7259            let child = self.match_eof()?;
7260            self.tree.add_child(&mut context, child);
7261        }
7262        let tree = self.rule_node(context);
7263        self.release_tree_scratch_if_idle();
7264        Ok(tree)
7265    }
7266
7267    /// Builds the parser error reported when no ATN path can reach the active
7268    /// rule stop state.
7269    fn recognition_error(
7270        &mut self,
7271        rule_index: usize,
7272        start_index: usize,
7273        expected: &ExpectedTokens,
7274    ) -> AntlrError {
7275        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
7276        self.input.seek(index);
7277        let current = self.input.lt(1);
7278        let line = current.as_ref().map(Token::line).unwrap_or_default();
7279        let column = current.as_ref().map(Token::column).unwrap_or_default();
7280        AntlrError::ParserError {
7281            line,
7282            column,
7283            message,
7284        }
7285    }
7286
7287    /// Builds the token index and ANTLR-compatible message for a failed rule.
7288    fn expected_error_message(
7289        &mut self,
7290        rule_index: usize,
7291        start_index: usize,
7292        expected: &ExpectedTokens,
7293    ) -> (usize, String) {
7294        let index = expected
7295            .index
7296            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
7297            .unwrap_or_else(|| self.input.index());
7298        self.input.seek(index);
7299        let current = self.input.lt(1);
7300        let message = if expected
7301            .no_viable
7302            .as_ref()
7303            .is_some_and(|no_viable| no_viable.error_index == index)
7304        {
7305            let start = expected
7306                .no_viable
7307                .as_ref()
7308                .map_or(start_index, |no_viable| no_viable.start_index);
7309            let text = display_input_text(&self.input.text(start, index));
7310            format!("no viable alternative at input '{text}'")
7311        } else if expected.symbols.is_empty() {
7312            if expected.index.is_some() {
7313                let found = current
7314                    .as_ref()
7315                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7316                if current
7317                    .as_ref()
7318                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7319                {
7320                    format!(
7321                        "missing {} at {found}",
7322                        self.expected_symbols_display(&expected.symbols)
7323                    )
7324                } else {
7325                    format!("mismatched input {found}")
7326                }
7327            } else {
7328                format!("no viable alternative while parsing rule {rule_index}")
7329            }
7330        } else {
7331            format!(
7332                "mismatched input {} expecting {}",
7333                current
7334                    .as_ref()
7335                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7336                self.expected_symbols_display(&expected.symbols)
7337            )
7338        };
7339        (index, message)
7340    }
7341
7342    /// Converts a failed child rule into a recovered outcome so the parent can
7343    /// continue after reporting the child diagnostic.
7344    fn child_rule_failure_recovery(
7345        &mut self,
7346        rule_index: usize,
7347        start_index: usize,
7348        sync_symbols: &BTreeSet<i32>,
7349        member_values: BTreeMap<usize, i64>,
7350        expected: &ExpectedTokens,
7351    ) -> Option<RecognizeOutcome> {
7352        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7353        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7354        let mut next_index = error_index;
7355        loop {
7356            let symbol = self.token_type_at(next_index);
7357            if sync_symbols.contains(&symbol) {
7358                if next_index == error_index {
7359                    return None;
7360                }
7361                break;
7362            }
7363            if symbol == TOKEN_EOF {
7364                break;
7365            }
7366            let after = self.consume_index(next_index, symbol);
7367            if after == next_index {
7368                break;
7369            }
7370            next_index = after;
7371        }
7372        let mut nodes = NodeSeqId::EMPTY;
7373        let error = self.arena_token_node(error_index, true);
7374        self.arena_prepend(&mut nodes, error);
7375        let diagnostics = self
7376            .recognition_arena
7377            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7378        Some(RecognizeOutcome {
7379            index: next_index,
7380            consumed_eof: false,
7381            alt_number: 0,
7382            member_values,
7383            return_values: BTreeMap::new(),
7384            diagnostics,
7385            decisions: Vec::new(),
7386            actions: Vec::new(),
7387            nodes,
7388        })
7389    }
7390
7391    /// Adapts the optional recovery result to the normal outcome list used by
7392    /// rule-call transitions.
7393    fn child_rule_failure_recovery_outcomes(
7394        &mut self,
7395        request: ChildRuleFailureRecovery<'_>,
7396    ) -> Vec<RecognizeOutcome> {
7397        let sync_symbols =
7398            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
7399        self.child_rule_failure_recovery(
7400            request.rule_index,
7401            request.start_index,
7402            &sync_symbols,
7403            request.member_values,
7404            request.expected,
7405        )
7406        .into_iter()
7407        .collect()
7408    }
7409
7410    /// Formats expected token types using ANTLR's single-token or set syntax.
7411    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
7412        expected_symbols_display(symbols, self.vocabulary())
7413    }
7414
7415    /// Returns the single-token deletion repair if the token after `index`
7416    /// satisfies the failed consuming transition.
7417    fn single_token_deletion(
7418        &mut self,
7419        transition: ParserTransition<'_>,
7420        index: usize,
7421        max_token_type: i32,
7422        expected_symbols: &BTreeSet<i32>,
7423    ) -> Option<(ParserDiagnostic, usize, i32)> {
7424        let current_symbol = self.token_type_at(index);
7425        if current_symbol == TOKEN_EOF {
7426            return None;
7427        }
7428        let next_index = self.consume_index(index, current_symbol);
7429        if next_index == index {
7430            return None;
7431        }
7432        let next_symbol = self.token_type_at(next_index);
7433        if !transition.matches(next_symbol, 1, max_token_type) {
7434            return None;
7435        }
7436        let transition_expected = transition_expected_symbols(transition, max_token_type);
7437        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7438            &transition_expected
7439        } else {
7440            expected_symbols
7441        });
7442        let current = self.token_at(index);
7443        let message = format!(
7444            "extraneous input {} expecting {expected_display}",
7445            current
7446                .as_ref()
7447                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7448        );
7449        Some((
7450            diagnostic_for_token(current, message),
7451            next_index,
7452            next_symbol,
7453        ))
7454    }
7455
7456    /// Returns the repair used when deleting the current token lets a recovery
7457    /// state continue with the following token.
7458    fn current_token_deletion(
7459        &mut self,
7460        index: usize,
7461        expected_symbols: &BTreeSet<i32>,
7462    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
7463        if expected_symbols.is_empty() {
7464            return None;
7465        }
7466        let current_symbol = self.token_type_at(index);
7467        if current_symbol == TOKEN_EOF {
7468            return None;
7469        }
7470        let current = self.token_at(index);
7471        let message = format!(
7472            "extraneous input {} expecting {}",
7473            current
7474                .as_ref()
7475                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7476            self.expected_symbols_display(expected_symbols)
7477        );
7478        let diagnostic = diagnostic_for_token(current, message);
7479        let mut skipped = Vec::new();
7480        let mut cursor = index;
7481        loop {
7482            let symbol = self.token_type_at(cursor);
7483            if symbol == TOKEN_EOF {
7484                return None;
7485            }
7486            skipped.push(cursor);
7487            let next_index = self.consume_index(cursor, symbol);
7488            if next_index == cursor {
7489                return None;
7490            }
7491            let next_symbol = self.token_type_at(next_index);
7492            if expected_symbols.contains(&next_symbol) {
7493                return Some((diagnostic, next_index, skipped));
7494            }
7495            cursor = next_index;
7496        }
7497    }
7498
7499    /// Returns the single-token insertion repair for a failed consuming
7500    /// transition. The caller validates the repair by continuing from the
7501    /// transition target at the same input index.
7502    fn single_token_insertion(
7503        &mut self,
7504        transition: ParserTransition<'_>,
7505        index: usize,
7506        max_token_type: i32,
7507        expected_symbols: &BTreeSet<i32>,
7508        follow_symbols: &BTreeSet<i32>,
7509    ) -> Option<(ParserDiagnostic, i32, String)> {
7510        let current_symbol = self.token_type_at(index);
7511        if !follow_symbols.contains(&current_symbol) {
7512            return None;
7513        }
7514        let transition_expected = transition_expected_symbols(transition, max_token_type);
7515        let token_type = transition_expected.iter().next().copied()?;
7516        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7517            &transition_expected
7518        } else {
7519            expected_symbols
7520        });
7521        let mut token_symbols = BTreeSet::new();
7522        token_symbols.insert(token_type);
7523        let missing_token_display = self.expected_symbols_display(&token_symbols);
7524        let current = self.token_at(index);
7525        let message = format!(
7526            "missing {expected_display} at {}",
7527            current
7528                .as_ref()
7529                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7530        );
7531        let text = format!("<missing {missing_token_display}>");
7532        Some((
7533            diagnostic_for_token(current.as_ref(), message),
7534            token_type,
7535            text,
7536        ))
7537    }
7538
7539    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
7540    /// skip the unexpected current token when the following token satisfies the
7541    /// transition that failed.
7542    fn fast_single_token_deletion_recovery(
7543        &mut self,
7544        recovery: FastRecoveryRequest<'_, '_>,
7545        predicate_context: Option<FastPredicateContext<'_>>,
7546    ) -> Vec<FastRecognizeOutcome> {
7547        let FastRecoveryRequest {
7548            atn,
7549            transition,
7550            expected_symbols,
7551            target,
7552            request,
7553            visiting,
7554            memo,
7555            expected,
7556        } = recovery;
7557        let FastRecognizeRequest {
7558            stop_state,
7559            index,
7560            rule_start_index,
7561            decision_start_index,
7562            precedence,
7563            depth,
7564            ..
7565        } = request;
7566        let Some((diagnostic, next_index, next_symbol)) =
7567            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7568        else {
7569            return Vec::new();
7570        };
7571        let after_next = self.consume_index(next_index, next_symbol);
7572        let empty_recovery = self.empty_recovery_symbols();
7573        self.recognize_state_fast(
7574            atn,
7575            FastRecognizeRequest {
7576                state_number: target,
7577                stop_state,
7578                index: after_next,
7579                rule_start_index,
7580                decision_start_index,
7581                precedence,
7582                depth: depth + 1,
7583                recovery_symbols: empty_recovery,
7584                recovery_state: None,
7585            },
7586            FastRecognizeScratch {
7587                predicate_context,
7588                visiting,
7589                memo,
7590                expected,
7591                native_depth: 0,
7592            },
7593        )
7594        .into_iter()
7595        .map(|mut outcome| {
7596            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7597            outcome.diagnostics = self
7598                .recognition_arena
7599                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7600            if self.fast_token_nodes_enabled {
7601                let token = self.arena_token_node(next_index, false);
7602                self.defer_fast_outcome_node(&mut outcome, token);
7603                let error = self.arena_token_node(index, true);
7604                self.defer_fast_outcome_node(&mut outcome, error);
7605            }
7606            outcome
7607        })
7608        .collect()
7609    }
7610
7611    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
7612    /// pretend the expected transition token was present and continue without
7613    /// consuming the current token.
7614    fn fast_single_token_insertion_recovery(
7615        &mut self,
7616        recovery: FastRecoveryRequest<'_, '_>,
7617        predicate_context: Option<FastPredicateContext<'_>>,
7618    ) -> Vec<FastRecognizeOutcome> {
7619        let FastRecoveryRequest {
7620            atn,
7621            transition,
7622            expected_symbols,
7623            target,
7624            request,
7625            visiting,
7626            memo,
7627            expected,
7628        } = recovery;
7629        let FastRecognizeRequest {
7630            stop_state,
7631            index,
7632            rule_start_index,
7633            decision_start_index,
7634            precedence,
7635            depth,
7636            ..
7637        } = request;
7638        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
7639        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7640            transition,
7641            index,
7642            atn.max_token_type(),
7643            &expected_symbols,
7644            &follow_symbols,
7645        ) else {
7646            return Vec::new();
7647        };
7648        let empty_recovery = self.empty_recovery_symbols();
7649        self.recognize_state_fast(
7650            atn,
7651            FastRecognizeRequest {
7652                state_number: target,
7653                stop_state,
7654                index,
7655                rule_start_index,
7656                decision_start_index,
7657                precedence,
7658                depth: depth + 1,
7659                recovery_symbols: empty_recovery,
7660                recovery_state: None,
7661            },
7662            FastRecognizeScratch {
7663                predicate_context,
7664                visiting,
7665                memo,
7666                expected,
7667                native_depth: 0,
7668            },
7669        )
7670        .into_iter()
7671        .map(|mut outcome| {
7672            outcome.diagnostics = self
7673                .recognition_arena
7674                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7675            let missing = self.arena_missing_token_node(token_type, index, text.clone());
7676            self.defer_fast_outcome_node(&mut outcome, missing);
7677            outcome
7678        })
7679        .collect()
7680    }
7681
7682    /// Retries the current fast-recognition state after deleting one
7683    /// unexpected token that precedes a valid loop or block continuation.
7684    fn fast_current_token_deletion_recovery(
7685        &mut self,
7686        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
7687        predicate_context: Option<FastPredicateContext<'_>>,
7688    ) -> Vec<FastRecognizeOutcome> {
7689        let FastCurrentTokenDeletionRequest {
7690            atn,
7691            expected_symbols,
7692            mut request,
7693            visiting,
7694            memo,
7695            expected,
7696        } = recovery;
7697        if request.index == request.rule_start_index {
7698            return Vec::new();
7699        }
7700        let Some((diagnostic, next_index, skipped)) =
7701            self.current_token_deletion(request.index, &expected_symbols)
7702        else {
7703            return Vec::new();
7704        };
7705        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7706        request.index = next_index;
7707        request.depth += 1;
7708        request.recovery_state = None;
7709        self.recognize_state_fast(
7710            atn,
7711            request,
7712            FastRecognizeScratch {
7713                predicate_context,
7714                visiting,
7715                memo,
7716                expected,
7717                native_depth: 0,
7718            },
7719        )
7720        .into_iter()
7721        .map(|mut outcome| {
7722            outcome.diagnostics = self
7723                .recognition_arena
7724                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7725            for index in skipped.iter().rev() {
7726                let error = self.arena_token_node(*index, true);
7727                self.defer_fast_outcome_node(&mut outcome, error);
7728            }
7729            outcome
7730        })
7731        .collect()
7732    }
7733
7734    /// Converts a failed child rule into a recovered fast-recognizer outcome so
7735    /// the parent can keep its child rule context and continue at a sync token.
7736    fn fast_child_rule_failure_recovery(
7737        &mut self,
7738        rule_index: usize,
7739        start_index: usize,
7740        sync_symbols: &BTreeSet<i32>,
7741        expected: &ExpectedTokens,
7742    ) -> Option<FastRecognizeOutcome> {
7743        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7744        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7745        let mut next_index = error_index;
7746        loop {
7747            let symbol = self.token_type_at(next_index);
7748            if sync_symbols.contains(&symbol) {
7749                if next_index == error_index {
7750                    return None;
7751                }
7752                break;
7753            }
7754            if symbol == TOKEN_EOF {
7755                break;
7756            }
7757            let after = self.consume_index(next_index, symbol);
7758            if after == next_index {
7759                break;
7760            }
7761            next_index = after;
7762        }
7763        let diagnostics = self
7764            .recognition_arena
7765            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7766        let mut nodes = NodeSeqId::EMPTY;
7767        if self.fast_token_nodes_enabled {
7768            let error = self.arena_token_node(error_index, true);
7769            self.arena_prepend(&mut nodes, error);
7770        }
7771        Some(FastRecognizeOutcome {
7772            index: next_index,
7773            consumed_eof: false,
7774            diagnostics,
7775            deferred_nodes: FastDeferredNodeId::EMPTY,
7776            nodes,
7777        })
7778    }
7779
7780    /// Adapts the optional child-rule recovery result to the fast-recognizer
7781    /// outcome list used by rule-call transitions.
7782    fn fast_child_rule_failure_recovery_outcomes(
7783        &mut self,
7784        request: FastChildRuleFailureRecoveryRequest<'_>,
7785    ) -> Vec<FastRecognizeOutcome> {
7786        let FastChildRuleFailureRecoveryRequest {
7787            atn,
7788            rule_index,
7789            start_index,
7790            follow_state,
7791            stop_state,
7792            expected,
7793        } = request;
7794        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
7795        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
7796            .into_iter()
7797            .collect()
7798    }
7799
7800    fn defer_fast_outcome_node(
7801        &mut self,
7802        outcome: &mut FastRecognizeOutcome,
7803        node: RecognizedNodeId,
7804    ) {
7805        if outcome.deferred_nodes.is_empty() {
7806            self.arena_prepend(&mut outcome.nodes, node);
7807            return;
7808        }
7809        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
7810        let fragment = self.recognition_arena.deferred_fragment(fragment);
7811        outcome.deferred_nodes = self
7812            .recognition_arena
7813            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
7814    }
7815
7816    fn materialize_fast_deferred_nodes(
7817        &mut self,
7818        root: FastDeferredNodeId,
7819        initial_suffix: NodeSeqId,
7820    ) -> NodeSeqId {
7821        if root.is_empty() {
7822            return initial_suffix;
7823        }
7824
7825        enum Frame {
7826            Visit(FastDeferredNodeId),
7827            ContinuePrefix(FastDeferredNodeId),
7828            FinishRule {
7829                rule: FastDeferredRule,
7830                parent_suffix: NodeSeqId,
7831            },
7832        }
7833
7834        let mut result = initial_suffix;
7835        let mut pending = Vec::with_capacity(16);
7836        pending.push(Frame::Visit(root));
7837        let mut fragment_nodes = Vec::new();
7838        while let Some(frame) = pending.pop() {
7839            match frame {
7840                Frame::Visit(deferred) => {
7841                    if deferred.is_empty() {
7842                        continue;
7843                    }
7844
7845                    match self.recognition_arena.deferred_node(deferred) {
7846                        FastDeferredNode::Fragment(sequence) => {
7847                            fragment_nodes.clear();
7848                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
7849                            while let Some(node) = fragment_nodes.pop() {
7850                                self.arena_prepend(&mut result, node);
7851                            }
7852                        }
7853                        FastDeferredNode::Rule(rule) => {
7854                            let rule = self.recognition_arena.deferred_rule(rule);
7855                            let parent_suffix = result;
7856                            result = rule.children;
7857                            pending.push(Frame::FinishRule {
7858                                rule,
7859                                parent_suffix,
7860                            });
7861                            pending.push(Frame::Visit(rule.deferred_children));
7862                        }
7863                        FastDeferredNode::Concat {
7864                            prefix,
7865                            suffix: deferred_suffix,
7866                        } => {
7867                            pending.push(Frame::ContinuePrefix(prefix));
7868                            pending.push(Frame::Visit(deferred_suffix));
7869                        }
7870                    }
7871                }
7872                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
7873                Frame::FinishRule {
7874                    rule,
7875                    parent_suffix,
7876                } => {
7877                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
7878                        rule_index: rule.rule_index,
7879                        invoking_state: rule.invoking_state,
7880                        alt_number: 0,
7881                        start_index: rule.start_index,
7882                        stop_index: rule.stop_index,
7883                        return_values: None,
7884                        children: result,
7885                    });
7886                    result = parent_suffix;
7887                    self.arena_prepend(&mut result, node);
7888                }
7889            }
7890        }
7891        result
7892    }
7893
7894    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) {
7895        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
7896        outcome.nodes = self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
7897    }
7898
7899    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
7900    /// heap work instead of the native call stack.
7901    fn recognize_repetition_fast(
7902        &mut self,
7903        atn: &Atn,
7904        request: &FastRecognizeRequest,
7905        shape: FastRepetitionShape,
7906        scratch: FastRecognizeScratch<'_, '_>,
7907    ) -> Vec<FastRecognizeOutcome> {
7908        let FastRecognizeScratch {
7909            predicate_context,
7910            visiting,
7911            memo,
7912            expected,
7913            native_depth,
7914        } = scratch;
7915        let lookahead = if self.fast_first_set_prefilter {
7916            atn.state(request.state_number).and_then(|state| {
7917                state
7918                    .rule_index()
7919                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
7920                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
7921            })
7922        } else {
7923            None
7924        };
7925        let mut work = Vec::with_capacity(2);
7926        push_fast_repetition_work(
7927            &mut work,
7928            shape,
7929            FastRepetitionPath {
7930                index: request.index,
7931                deferred_nodes: FastDeferredNodeId::EMPTY,
7932                diagnostics: DiagnosticSeqId::EMPTY,
7933                consumed_eof: false,
7934            },
7935            lookahead.as_deref(),
7936            self.token_type_at(request.index),
7937        );
7938        let mut coordinates = FastRepetitionCoordinates::new(request.index);
7939        let mut outcomes = Vec::new();
7940        while let Some(item) = work.pop() {
7941            match item {
7942                FastRepetitionWork::Enter(path) => {
7943                    if !coordinates.insert_entered(path) {
7944                        continue;
7945                    }
7946                    let body_outcomes = self.recognize_state_fast(
7947                        atn,
7948                        FastRecognizeRequest {
7949                            state_number: shape.enter_target,
7950                            stop_state: shape.body_stop_state,
7951                            index: path.index,
7952                            rule_start_index: request.rule_start_index,
7953                            decision_start_index: request.decision_start_index,
7954                            precedence: request.precedence,
7955                            depth: request.depth.saturating_add(1),
7956                            recovery_symbols: Rc::clone(&request.recovery_symbols),
7957                            recovery_state: request.recovery_state,
7958                        },
7959                        FastRecognizeScratch {
7960                            predicate_context,
7961                            visiting: &mut *visiting,
7962                            memo: &mut *memo,
7963                            expected: &mut *expected,
7964                            native_depth: native_depth + 1,
7965                        },
7966                    );
7967                    for body in body_outcomes.into_iter().rev() {
7968                        // ANTLR rejects nullable repetition bodies. Keep the
7969                        // interpreter bounded for malformed or recovered ATNs
7970                        // by mirroring the existing same-coordinate cycle cut.
7971                        if body.index <= path.index {
7972                            continue;
7973                        }
7974                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
7975                        let body_nodes = self
7976                            .recognition_arena
7977                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
7978                        let deferred_nodes = self
7979                            .recognition_arena
7980                            .concat_deferred_nodes(path.deferred_nodes, body_nodes);
7981                        let next_path = FastRepetitionPath {
7982                            index: body.index,
7983                            deferred_nodes,
7984                            diagnostics: self
7985                                .recognition_arena
7986                                .concat_diagnostics(path.diagnostics, body.diagnostics),
7987                            consumed_eof: path.consumed_eof || body.consumed_eof,
7988                        };
7989                        let symbol = self.token_type_at(next_path.index);
7990                        push_fast_repetition_work(
7991                            &mut work,
7992                            shape,
7993                            next_path,
7994                            lookahead.as_deref(),
7995                            symbol,
7996                        );
7997                    }
7998                }
7999                FastRepetitionWork::Exit(path) => {
8000                    if !coordinates.insert_exited(path) {
8001                        continue;
8002                    }
8003                    let suffixes = self.recognize_state_fast(
8004                        atn,
8005                        FastRecognizeRequest {
8006                            state_number: shape.exit_target,
8007                            stop_state: request.stop_state,
8008                            index: path.index,
8009                            rule_start_index: request.rule_start_index,
8010                            decision_start_index: request.decision_start_index,
8011                            precedence: request.precedence,
8012                            depth: request.depth.saturating_add(1),
8013                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8014                            recovery_state: request.recovery_state,
8015                        },
8016                        FastRecognizeScratch {
8017                            predicate_context,
8018                            visiting: &mut *visiting,
8019                            memo: &mut *memo,
8020                            expected: &mut *expected,
8021                            native_depth: native_depth + 1,
8022                        },
8023                    );
8024                    for mut outcome in suffixes {
8025                        outcome.deferred_nodes = self
8026                            .recognition_arena
8027                            .concat_deferred_nodes(path.deferred_nodes, outcome.deferred_nodes);
8028                        outcome.diagnostics = self
8029                            .recognition_arena
8030                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8031                        outcome.consumed_eof |= path.consumed_eof;
8032                        outcomes.push(outcome);
8033                    }
8034                }
8035            }
8036        }
8037        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8038        outcomes
8039    }
8040
8041    /// Attempts to reach `stop_state` from `state_number` without committing
8042    /// token consumption to the parser's public stream position.
8043    fn recognize_state_fast(
8044        &mut self,
8045        atn: &Atn,
8046        request: FastRecognizeRequest,
8047        scratch: FastRecognizeScratch<'_, '_>,
8048    ) -> Vec<FastRecognizeOutcome> {
8049        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
8050            return self.recognize_state_fast_inner(atn, request, scratch);
8051        }
8052        self.recognize_state_fast_checked(atn, request, scratch)
8053    }
8054
8055    #[inline(never)]
8056    fn recognize_state_fast_checked(
8057        &mut self,
8058        atn: &Atn,
8059        request: FastRecognizeRequest,
8060        mut scratch: FastRecognizeScratch<'_, '_>,
8061    ) -> Vec<FastRecognizeOutcome> {
8062        scratch.native_depth = 1;
8063        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
8064            self.recognize_state_fast_inner(atn, request, scratch)
8065        })
8066    }
8067
8068    #[allow(clippy::too_many_lines)]
8069    fn recognize_state_fast_inner(
8070        &mut self,
8071        atn: &Atn,
8072        request: FastRecognizeRequest,
8073        scratch: FastRecognizeScratch<'_, '_>,
8074    ) -> Vec<FastRecognizeOutcome> {
8075        #[cfg(feature = "perf-counters")]
8076        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8077        let FastRecognizeScratch {
8078            predicate_context,
8079            visiting,
8080            memo,
8081            expected,
8082            native_depth,
8083        } = scratch;
8084        let FastRecognizeRequest {
8085            mut state_number,
8086            stop_state,
8087            mut index,
8088            rule_start_index,
8089            decision_start_index,
8090            precedence,
8091            mut depth,
8092            recovery_symbols,
8093            recovery_state,
8094        } = request;
8095        let max_token_type = atn.max_token_type();
8096        // Walk straight-line epsilon chains in a loop instead of recursing
8097        // into `recognize_state_fast` for each intermediate state. ATN
8098        // serialization places long sequences of `BasicBlock` epsilon
8099        // transitions between decisions: turning that chain into a loop
8100        // collapses many recursive calls (and their memo lookups, vec
8101        // allocations, and visit-set churn) into a single function frame.
8102        // The loop exits as soon as we hit the original state's logic
8103        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8104        // precedence) so existing fanout, recovery, and memoization still
8105        // apply unchanged.
8106        //
8107        // The inline case also handles single-atom-match states on the
8108        // happy-pass path: when the lone consuming transition matches the
8109        // current lookahead, advance the index and continue without paying
8110        // for a full `recognize_state_fast` recursion. We track tokens we
8111        // consumed inline in `inline_consumed_tokens` so they can be
8112        // prepended onto the eventual outcome list once we hit a state
8113        // whose handling falls outside this fast loop.
8114        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8115        let mut inline_consumed_eof = false;
8116        loop {
8117            if depth > RECOGNITION_DEPTH_LIMIT {
8118                return Vec::new();
8119            }
8120            if state_number == stop_state {
8121                let mut nodes = NodeSeqId::EMPTY;
8122                if self.fast_token_nodes_enabled {
8123                    for token_index in inline_consumed_tokens.iter().rev() {
8124                        let token = self.arena_token_node(*token_index, false);
8125                        self.arena_prepend(&mut nodes, token);
8126                    }
8127                }
8128                return vec![FastRecognizeOutcome {
8129                    index,
8130                    consumed_eof: inline_consumed_eof,
8131                    diagnostics: DiagnosticSeqId::EMPTY,
8132                    deferred_nodes: FastDeferredNodeId::EMPTY,
8133                    nodes,
8134                }];
8135            }
8136            let Some(state) = atn.state(state_number) else {
8137                return Vec::new();
8138            };
8139            let transitions = state.transitions();
8140            if transitions.len() == 1 && !state.precedence_rule_decision() {
8141                let transition = transitions
8142                    .first()
8143                    .expect("single transition checked above");
8144                let transition_kind = transition.kind();
8145                let target = transition.target();
8146                match transition_kind {
8147                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
8148                        if left_recursive_boundary(atn, state, target).is_none() =>
8149                    {
8150                        #[cfg(feature = "perf-counters")]
8151                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8152                        state_number = target;
8153                        depth += 1;
8154                        continue;
8155                    }
8156                    ParserTransitionKind::Predicate
8157                        if left_recursive_boundary(atn, state, target).is_none() =>
8158                    {
8159                        #[cfg(feature = "perf-counters")]
8160                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8161                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
8162                        {
8163                            record_predicate_no_viable(expected, decision_start_index, index);
8164                            return Vec::new();
8165                        }
8166                        state_number = target;
8167                        depth += 1;
8168                        continue;
8169                    }
8170                    ParserTransitionKind::Precedence
8171                        if packed_i32(transition.arg0()) >= precedence
8172                            && left_recursive_boundary(atn, state, target).is_none() =>
8173                    {
8174                        #[cfg(feature = "perf-counters")]
8175                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8176                        state_number = target;
8177                        depth += 1;
8178                        continue;
8179                    }
8180                    // Single-atom / range / set / wildcard / not-set states
8181                    // are common (~17K of ~125K calls on C#) and almost
8182                    // always succeed in pass 1: no fanout, no recovery, no
8183                    // diagnostics. Inline the token match and continue
8184                    // walking instead of recursing — the recursive path
8185                    // would just allocate a Vec, build one outcome, prepend
8186                    // a Token node, and return. Skip pass 2 (recovery
8187                    // enabled): there the failure branch matters and the
8188                    // existing recursive code records expected symbols.
8189                    ParserTransitionKind::Atom
8190                    | ParserTransitionKind::Range
8191                    | ParserTransitionKind::Set
8192                    | ParserTransitionKind::NotSet
8193                    | ParserTransitionKind::Wildcard
8194                        if !self.fast_recovery_enabled =>
8195                    {
8196                        let symbol = self.token_type_at(index);
8197                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8198                            #[cfg(feature = "perf-counters")]
8199                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8200                            if self.fast_token_nodes_enabled {
8201                                inline_consumed_tokens.push(index);
8202                            }
8203                            inline_consumed_eof |= symbol == TOKEN_EOF;
8204                            index = self.consume_index(index, symbol);
8205                            state_number = target;
8206                            depth += 1;
8207                            continue;
8208                        }
8209                        // Fall through to break and let the regular
8210                        // body handle the no-match case (returns empty).
8211                    }
8212                    _ => {}
8213                }
8214            }
8215            break;
8216        }
8217        // If we collected token nodes inline but bail to the recursive
8218        // body (decision state, rule call, etc.), the outcomes returned
8219        // below will need those token nodes prepended.
8220        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
8221        let Some(state) = atn.state(state_number) else {
8222            return Vec::new();
8223        };
8224        let transitions = state.transitions();
8225        let transition_count = transitions.len();
8226        if !self.fast_recovery_enabled
8227            && let Some(shape) = fast_repetition_shape(atn, state)
8228        {
8229            let mut outcomes = self.recognize_repetition_fast(
8230                atn,
8231                &FastRecognizeRequest {
8232                    state_number,
8233                    stop_state,
8234                    index,
8235                    rule_start_index,
8236                    decision_start_index,
8237                    precedence,
8238                    depth,
8239                    recovery_symbols: Rc::clone(&recovery_symbols),
8240                    recovery_state,
8241                },
8242                shape,
8243                FastRecognizeScratch {
8244                    predicate_context,
8245                    visiting: &mut *visiting,
8246                    memo: &mut *memo,
8247                    expected: &mut *expected,
8248                    native_depth: native_depth + 1,
8249                },
8250            );
8251            if inline_pending {
8252                for outcome in &mut outcomes {
8253                    outcome.consumed_eof |= inline_consumed_eof;
8254                    if self.fast_token_nodes_enabled {
8255                        for token_index in inline_consumed_tokens.iter().rev() {
8256                            let token = self.arena_token_node(*token_index, false);
8257                            self.defer_fast_outcome_node(outcome, token);
8258                        }
8259                    }
8260                }
8261            }
8262            return outcomes;
8263        }
8264        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
8265        // fields and the rule/decision boundary indices are pure plumbing —
8266        // they only affect the recovery branch and the no-viable diagnostic
8267        // recording, neither of which fires when recovery is off. Zeroing
8268        // them in the memo key collapses calls that visit the same
8269        // `(state, index)` from different rule-call sites onto one cache
8270        // entry, which is the dominant cost on large grammars (e.g. C#) where
8271        // many rules eventually delegate into the same `expression` /
8272        // `primary_expression` / `type` branches.
8273        let key = if self.fast_recovery_enabled {
8274            FastRecognizeKey {
8275                state_number,
8276                stop_state,
8277                index,
8278                rule_start_index,
8279                decision_start_index,
8280                precedence,
8281                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
8282                recovery_state,
8283            }
8284        } else {
8285            FastRecognizeKey {
8286                state_number,
8287                stop_state,
8288                index,
8289                rule_start_index: 0,
8290                decision_start_index: None,
8291                precedence,
8292                recovery_symbols_id: 0,
8293                recovery_state: None,
8294            }
8295        };
8296        // Once the clean-pass probe has established that coordinates do not
8297        // repeat, stop paying for the full memo table. Recovery always keeps
8298        // memoization because cached failures carry diagnostics, while
8299        // repeat-heavy clean parses promote before reaching sparse mode.
8300        let memo_lookup_enabled = self.fast_recovery_enabled
8301            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
8302        if memo_lookup_enabled {
8303            if let Some(outcomes) = memo.get(&key) {
8304                #[cfg(feature = "perf-counters")]
8305                {
8306                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
8307                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
8308                }
8309                // Materialize a fresh `Vec` from the cached slice; the caller
8310                // mutates per-outcome state (eof flags, prepended nodes) so we
8311                // can't hand them the shared backing.
8312                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
8313                    let inline_eof = inline_consumed_eof;
8314                    let inline_tokens = &inline_consumed_tokens;
8315                    return outcomes
8316                        .iter()
8317                        .copied()
8318                        .map(|mut outcome| {
8319                            if inline_eof {
8320                                outcome.consumed_eof = true;
8321                            }
8322                            if self.fast_token_nodes_enabled {
8323                                for token_index in inline_tokens.iter().rev() {
8324                                    let token = self.arena_token_node(*token_index, false);
8325                                    self.defer_fast_outcome_node(&mut outcome, token);
8326                                }
8327                            }
8328                            outcome
8329                        })
8330                        .collect();
8331                }
8332                return outcomes.to_vec();
8333            }
8334            #[cfg(feature = "perf-counters")]
8335            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
8336        }
8337
8338        // Cycle detection: clean recognition keeps the narrow static cycle
8339        // guard used on hot paths. Recovery needs the broader epsilon-state
8340        // guard because an otherwise non-nullable loop body can recover as an
8341        // empty child at EOF and re-enter the loop at the same token.
8342        let needs_cycle_guard = if self.fast_recovery_enabled {
8343            transitions.iter().any(ParserTransition::is_epsilon)
8344        } else {
8345            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
8346        };
8347        #[cfg(feature = "perf-counters")]
8348        if needs_cycle_guard {
8349            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
8350        } else {
8351            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
8352            match state
8353                .transitions()
8354                .first()
8355                .expect("single-transition path requires one transition")
8356                .data()
8357            {
8358                Transition::Rule { .. } => {
8359                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
8360                }
8361                Transition::Atom { .. }
8362                | Transition::Range { .. }
8363                | Transition::Set { .. }
8364                | Transition::NotSet { .. }
8365                | Transition::Wildcard { .. } => {
8366                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
8367                }
8368                _ => {
8369                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
8370                }
8371            }
8372        }
8373        let has_inserted_cycle_guard = if needs_cycle_guard {
8374            if !visiting.insert(key.clone()) {
8375                #[cfg(feature = "perf-counters")]
8376                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
8377                return Vec::new();
8378            }
8379            true
8380        } else {
8381            false
8382        };
8383        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
8384            Some(index)
8385        } else {
8386            decision_start_index
8387        };
8388        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
8389            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
8390        } else {
8391            (Rc::clone(&recovery_symbols), recovery_state)
8392        };
8393
8394        // Lookahead-based pruning. At a multi-alternative state we cache the
8395        // look-1 set of every outgoing transition; on visit we keep only the
8396        // transitions whose look-1 can accept the current lookahead (or that
8397        // can be reached without consuming and so could legitimately match a
8398        // shorter input). This is the main speedup vs. blind speculative
8399        // recursion: it lets each visit fan out only to the alternatives that
8400        // could possibly contribute a clean parse, mirroring the SLL phase of
8401        // ANTLR's adaptive prediction.
8402        //
8403        // Pruning is skipped at:
8404        //   * rule-start states (a child rule call may need every internal
8405        //     transition to surface single-token recovery diagnostics that
8406        //     ANTLR's reference parser emits at the rule's first consuming
8407        //     transition; the FIRST-set retry path turns the prefilter off
8408        //     entirely so let's keep this lightweight too),
8409        //   * left-recursive precedence loops (the precedence transition's
8410        //     gating is dynamic),
8411        //   * states with too few alternatives to benefit.
8412        let lookahead_filter = if transition_count > 1
8413            && self.fast_first_set_prefilter
8414            && !state.precedence_rule_decision()
8415            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
8416        {
8417            state
8418                .rule_index()
8419                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8420                .map(|rule_stop| {
8421                    let symbol = self.token_type_at(index);
8422                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
8423                    (symbol, entry)
8424                })
8425        } else {
8426            None
8427        };
8428        // LL(1) fast path: when the FIRST sets for the decision are disjoint
8429        // and none is nullable, the lookahead deterministically selects one
8430        // alternative. The recursive recognizer can then commit to that single
8431        // alt without iterating every transition through `should_skip_via_lookahead`
8432        // — saving (transition_count - 1) filter probes per visit.
8433        //
8434        // Result is cached per `(state, lookahead_token)` on the parser
8435        // instance, so subsequent visits skip the FIRST-set scan entirely.
8436        let ll1_only_alt: Option<usize> = if transition_count > 1
8437            && let Some((symbol, entry)) = lookahead_filter.as_ref()
8438        {
8439            let key = (state.state_number(), *symbol);
8440            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
8441                cached
8442            } else {
8443                let result = ll1_unique_alt(entry, *symbol);
8444                self.ll1_decision_cache.insert(key, result);
8445                result
8446            }
8447        } else {
8448            None
8449        };
8450        let lookahead_filter = lookahead_filter.as_ref();
8451        // Pre-size only when we expect at least one outcome to land — most
8452        // single-transition fall-throughs (the loop above didn't catch
8453        // because they're atom/rule/predicate) push at most one entry, so
8454        // reserving one slot avoids a reallocation while keeping the
8455        // unused-slot waste at one element.
8456        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
8457        for (transition_index, transition) in transitions.iter().enumerate() {
8458            if let Some(alt) = ll1_only_alt {
8459                // LL(1) determinism: skip every alt except the chosen one.
8460                if alt != transition_index {
8461                    continue;
8462                }
8463            }
8464            let transition_kind = transition.kind();
8465            if ll1_only_alt.is_none()
8466                && should_skip_via_lookahead(
8467                    transition_kind,
8468                    transition_index,
8469                    lookahead_filter,
8470                    index,
8471                    self.fast_recovery_enabled,
8472                    expected,
8473                )
8474            {
8475                continue;
8476            }
8477            let target = transition.target();
8478            match transition_kind {
8479                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
8480                    #[cfg(feature = "perf-counters")]
8481                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8482                    let boundary = left_recursive_boundary(atn, state, target);
8483                    outcomes.extend(
8484                        self.recognize_state_fast(
8485                            atn,
8486                            FastRecognizeRequest {
8487                                state_number: target,
8488                                stop_state,
8489                                index,
8490                                rule_start_index,
8491                                decision_start_index: next_decision_start_index,
8492                                precedence,
8493                                depth: depth + 1,
8494                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8495                                recovery_state: epsilon_recovery_state,
8496                            },
8497                            FastRecognizeScratch {
8498                                predicate_context,
8499                                visiting,
8500                                memo,
8501                                expected,
8502                                native_depth: native_depth + 1,
8503                            },
8504                        )
8505                        .into_iter()
8506                        .map(|mut outcome| {
8507                            if let Some(rule_index) = boundary {
8508                                let boundary = self.arena_boundary_node(rule_index);
8509                                self.defer_fast_outcome_node(&mut outcome, boundary);
8510                            }
8511                            outcome
8512                        }),
8513                    );
8514                }
8515                ParserTransitionKind::Predicate => {
8516                    #[cfg(feature = "perf-counters")]
8517                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8518                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
8519                        let boundary = left_recursive_boundary(atn, state, target);
8520                        outcomes.extend(
8521                            self.recognize_state_fast(
8522                                atn,
8523                                FastRecognizeRequest {
8524                                    state_number: target,
8525                                    stop_state,
8526                                    index,
8527                                    rule_start_index,
8528                                    decision_start_index: next_decision_start_index,
8529                                    precedence,
8530                                    depth: depth + 1,
8531                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8532                                    recovery_state: epsilon_recovery_state,
8533                                },
8534                                FastRecognizeScratch {
8535                                    predicate_context,
8536                                    visiting,
8537                                    memo,
8538                                    expected,
8539                                    native_depth: native_depth + 1,
8540                                },
8541                            )
8542                            .into_iter()
8543                            .map(|mut outcome| {
8544                                if let Some(rule_index) = boundary {
8545                                    let boundary = self.arena_boundary_node(rule_index);
8546                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8547                                }
8548                                outcome
8549                            }),
8550                        );
8551                    } else {
8552                        record_predicate_no_viable(expected, next_decision_start_index, index);
8553                    }
8554                }
8555                ParserTransitionKind::Precedence => {
8556                    let transition_precedence = packed_i32(transition.arg0());
8557                    if transition_precedence >= precedence {
8558                        let boundary = left_recursive_boundary(atn, state, target);
8559                        outcomes.extend(
8560                            self.recognize_state_fast(
8561                                atn,
8562                                FastRecognizeRequest {
8563                                    state_number: target,
8564                                    stop_state,
8565                                    index,
8566                                    rule_start_index,
8567                                    decision_start_index: next_decision_start_index,
8568                                    precedence,
8569                                    depth: depth + 1,
8570                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8571                                    recovery_state: epsilon_recovery_state,
8572                                },
8573                                FastRecognizeScratch {
8574                                    predicate_context,
8575                                    visiting,
8576                                    memo,
8577                                    expected,
8578                                    native_depth: native_depth + 1,
8579                                },
8580                            )
8581                            .into_iter()
8582                            .map(|mut outcome| {
8583                                if let Some(rule_index) = boundary {
8584                                    let boundary = self.arena_boundary_node(rule_index);
8585                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8586                                }
8587                                outcome
8588                            }),
8589                        );
8590                    }
8591                }
8592                ParserTransitionKind::Rule => {
8593                    let rule_index = transition.arg0() as usize;
8594                    let follow_state = transition.arg1() as usize;
8595                    let rule_precedence = packed_i32(transition.arg2());
8596                    #[cfg(feature = "perf-counters")]
8597                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
8598                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
8599                        continue;
8600                    };
8601                    // Lookahead-based pruning. The recognizer would otherwise
8602                    // explore every speculative rule call, producing exponential
8603                    // work on grammars with many epsilon-reachable rules. When
8604                    // the rule is non-nullable and its FIRST set excludes the
8605                    // current lookahead, recursion can't find a clean path
8606                    // *through this rule*. Skipping is only safe if some sibling
8607                    // transition can still consume the lookahead — otherwise the
8608                    // rule call is the sole continuation and must run so the
8609                    // single-token insertion / deletion recovery inside the
8610                    // called rule can fire (mirroring ANTLR's reference behavior
8611                    // of conjuring a missing token at child-rule entry).
8612                    let symbol = self.token_type_at(index);
8613                    if self.fast_first_set_prefilter {
8614                        // Probe the shared cross-parse cache first; build
8615                        // the entry on miss and intern it there. The
8616                        // computation is purely a function of the ATN, so
8617                        // the cached entry is reused across parses (and
8618                        // freshly-instantiated parser values that share
8619                        // the same `&'static Atn`).
8620                        //
8621                        // `rule_first_set` returns the computed entry
8622                        // directly — it intentionally skips inserting into
8623                        // the cache when the FIRST-set walk hit a cycle, so
8624                        // we cannot assume the entry is in the cache after
8625                        // computing it.
8626                        let first = self.cached_rule_first_set(atn, target, child_stop);
8627                        if should_skip_rule_via_first_set(
8628                            &first,
8629                            symbol,
8630                            self.fast_recovery_enabled,
8631                            index,
8632                            expected,
8633                        ) {
8634                            continue;
8635                        }
8636                    }
8637                    let expected_before_child =
8638                        self.fast_recovery_enabled.then(|| expected.clone());
8639                    let mut children = self.recognize_state_fast(
8640                        atn,
8641                        FastRecognizeRequest {
8642                            state_number: target,
8643                            stop_state: child_stop,
8644                            index,
8645                            rule_start_index: index,
8646                            decision_start_index: None,
8647                            precedence: rule_precedence,
8648                            depth: depth + 1,
8649                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8650                            recovery_state: epsilon_recovery_state,
8651                        },
8652                        FastRecognizeScratch {
8653                            predicate_context,
8654                            visiting,
8655                            memo,
8656                            expected,
8657                            native_depth: native_depth + 1,
8658                        },
8659                    );
8660                    if children.is_empty() && self.fast_recovery_enabled {
8661                        children = self.fast_child_rule_failure_recovery_outcomes(
8662                            FastChildRuleFailureRecoveryRequest {
8663                                atn,
8664                                rule_index,
8665                                start_index: index,
8666                                follow_state,
8667                                stop_state,
8668                                expected,
8669                            },
8670                        );
8671                    }
8672                    if let Some(expected_before_child) = expected_before_child {
8673                        if children
8674                            .iter()
8675                            .any(|child| child.diagnostics.is_empty() && child.index > index)
8676                        {
8677                            *expected = expected_before_child;
8678                        }
8679                    }
8680                    for child in children {
8681                        let child_index = child.index;
8682                        let child_consumed_eof = child.consumed_eof;
8683                        let child_diagnostics = child.diagnostics;
8684                        let empty_recovery = self.empty_recovery_symbols();
8685                        let follow_outcomes = self.recognize_state_fast(
8686                            atn,
8687                            FastRecognizeRequest {
8688                                state_number: follow_state,
8689                                stop_state,
8690                                index: child_index,
8691                                rule_start_index,
8692                                decision_start_index: next_decision_start_index,
8693                                precedence,
8694                                depth: depth + 1,
8695                                recovery_symbols: empty_recovery,
8696                                recovery_state: None,
8697                            },
8698                            FastRecognizeScratch {
8699                                predicate_context,
8700                                visiting,
8701                                memo,
8702                                expected,
8703                                native_depth: native_depth + 1,
8704                            },
8705                        );
8706                        if follow_outcomes.is_empty() {
8707                            continue;
8708                        }
8709                        let child_stop_index =
8710                            self.rule_stop_token_index(child_index, child_consumed_eof);
8711                        let child_node = self.build_parse_trees.then(|| {
8712                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
8713                                rule_index: u32::try_from(rule_index)
8714                                    .expect("rule index fits in u32"),
8715                                invoking_state: i32::try_from(invoking_state_number(state_number))
8716                                    .expect("invoking state fits in i32"),
8717                                start_index: u32::try_from(index)
8718                                    .expect("rule start index fits in u32"),
8719                                stop_index: child_stop_index.map(|stop_index| {
8720                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
8721                                }),
8722                                deferred_children: child.deferred_nodes,
8723                                children: child.nodes,
8724                            })
8725                        });
8726                        let child_diags_empty = child_diagnostics.is_empty();
8727                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
8728                            outcome.consumed_eof |= child_consumed_eof;
8729                            // Skip the prepend dance when there's nothing to
8730                            // merge from the child — common case in pass 1.
8731                            if !child_diags_empty {
8732                                outcome.diagnostics = self
8733                                    .recognition_arena
8734                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
8735                            }
8736                            if let Some(child_node) = child_node {
8737                                outcome.deferred_nodes = self
8738                                    .recognition_arena
8739                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
8740                            }
8741                            outcome
8742                        }));
8743                    }
8744                }
8745                ParserTransitionKind::Atom
8746                | ParserTransitionKind::Range
8747                | ParserTransitionKind::Set
8748                | ParserTransitionKind::NotSet
8749                | ParserTransitionKind::Wildcard => {
8750                    #[cfg(feature = "perf-counters")]
8751                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8752                    let symbol = self.token_type_at(index);
8753                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8754                        let next_index = self.consume_index(index, symbol);
8755                        let empty_recovery = self.empty_recovery_symbols();
8756                        outcomes.extend(
8757                            self.recognize_state_fast(
8758                                atn,
8759                                FastRecognizeRequest {
8760                                    state_number: target,
8761                                    stop_state,
8762                                    index: next_index,
8763                                    rule_start_index,
8764                                    decision_start_index: next_decision_start_index,
8765                                    precedence,
8766                                    depth: depth + 1,
8767                                    recovery_symbols: empty_recovery,
8768                                    recovery_state: None,
8769                                },
8770                                FastRecognizeScratch {
8771                                    predicate_context,
8772                                    visiting,
8773                                    memo,
8774                                    expected,
8775                                    native_depth: native_depth + 1,
8776                                },
8777                            )
8778                            .into_iter()
8779                            .map(|mut outcome| {
8780                                outcome.consumed_eof |= symbol == TOKEN_EOF;
8781                                if self.fast_token_nodes_enabled {
8782                                    let token = self.arena_token_node(index, false);
8783                                    self.defer_fast_outcome_node(&mut outcome, token);
8784                                }
8785                                outcome
8786                            }),
8787                        );
8788                    } else {
8789                        if !self.fast_recovery_enabled {
8790                            // In pass 1 there is no recovery to attempt; the
8791                            // recovery branch below would never run, and the
8792                            // `expected_symbols` computation is just there
8793                            // to gate that branch. Skipping it eliminates
8794                            // ~1× `state_expected_symbols` lookup per failed
8795                            // atom transition (≈82K on mono-statement.cs)
8796                            // for zero observable behavior change.
8797                            continue;
8798                        }
8799                        let expected_symbols = fast_recovery_expected_symbols(
8800                            self,
8801                            atn,
8802                            state.state_number(),
8803                            &recovery_symbols,
8804                        );
8805                        if expected_symbols.contains(&symbol) {
8806                            continue;
8807                        }
8808                        {
8809                            expected.record_transition(index, transition, max_token_type);
8810                            record_no_viable_if_ambiguous(
8811                                expected,
8812                                next_decision_start_index,
8813                                index,
8814                            );
8815                            outcomes.extend(self.fast_single_token_deletion_recovery(
8816                                FastRecoveryRequest {
8817                                    atn,
8818                                    transition,
8819                                    expected_symbols: Rc::clone(&expected_symbols),
8820                                    target,
8821                                    request: FastRecognizeRequest {
8822                                        state_number,
8823                                        stop_state,
8824                                        index,
8825                                        rule_start_index,
8826                                        decision_start_index,
8827                                        precedence,
8828                                        depth,
8829                                        recovery_symbols: Rc::clone(&recovery_symbols),
8830                                        recovery_state,
8831                                    },
8832                                    visiting,
8833                                    memo,
8834                                    expected,
8835                                },
8836                                predicate_context,
8837                            ));
8838                            if !state_is_left_recursive_rule(atn, state) {
8839                                outcomes.extend(self.fast_single_token_insertion_recovery(
8840                                    FastRecoveryRequest {
8841                                        atn,
8842                                        transition,
8843                                        expected_symbols: Rc::clone(&expected_symbols),
8844                                        target,
8845                                        request: FastRecognizeRequest {
8846                                            state_number,
8847                                            stop_state,
8848                                            index,
8849                                            rule_start_index,
8850                                            decision_start_index,
8851                                            precedence,
8852                                            depth,
8853                                            recovery_symbols: Rc::clone(&recovery_symbols),
8854                                            recovery_state,
8855                                        },
8856                                        visiting,
8857                                        memo,
8858                                        expected,
8859                                    },
8860                                    predicate_context,
8861                                ));
8862                            }
8863                            outcomes.extend(self.fast_current_token_deletion_recovery(
8864                                FastCurrentTokenDeletionRequest {
8865                                    atn,
8866                                    expected_symbols,
8867                                    request: FastRecognizeRequest {
8868                                        state_number,
8869                                        stop_state,
8870                                        index,
8871                                        rule_start_index,
8872                                        decision_start_index,
8873                                        precedence,
8874                                        depth,
8875                                        recovery_symbols: Rc::clone(&recovery_symbols),
8876                                        recovery_state,
8877                                    },
8878                                    visiting,
8879                                    memo,
8880                                    expected,
8881                                },
8882                                predicate_context,
8883                            ));
8884                        }
8885                    }
8886                }
8887            }
8888        }
8889
8890        if has_inserted_cycle_guard {
8891            visiting.remove(&key);
8892        }
8893        if matches!(
8894            self.prediction_mode,
8895            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
8896        ) && self.fast_recovery_enabled
8897        {
8898            // Without recovery enabled every outcome already has empty
8899            // diagnostics, so the discard pass is a no-op — skipping it
8900            // saves an iter+retain on each of the ~1M visits.
8901            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
8902        }
8903        if self.fast_recovery_enabled {
8904            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
8905        } else {
8906            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8907        }
8908        // Skip memoization for single-transition states whose outcome is
8909        // unambiguous: they only get re-entered if the caller revisits the
8910        // exact same call site, which is rare since the loop above already
8911        // collapsed straight-line epsilon walks. Multi-alternative states
8912        // are where backtracking actually revisits the same coordinate, so
8913        // we still memoize there. With recovery on we keep the existing
8914        // memoization unconditionally because the recovery branch may
8915        // record diagnostics that the cache must surface to repeated
8916        // failed visits.
8917        let should_memoize = self.fast_recovery_enabled
8918            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
8919        // Apply inline pending state to each outcome before returning.
8920        // Tokens consumed inline by the loop-collapse don't appear in the
8921        // recursive recognizer's output, so we need to prepend them here.
8922        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
8923            if inline_consumed_eof {
8924                outcome.consumed_eof = true;
8925            }
8926            if !inline_consumed_tokens.is_empty() {
8927                for token_index in inline_consumed_tokens.iter().rev() {
8928                    let token = self.arena_token_node(*token_index, false);
8929                    self.defer_fast_outcome_node(&mut outcome, token);
8930                }
8931            }
8932            outcome
8933        };
8934        if should_memoize {
8935            #[cfg(feature = "perf-counters")]
8936            {
8937                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
8938                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
8939                match outcomes.len() {
8940                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8941                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8942                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8943                }
8944            }
8945            // The memo is keyed by the loop-exit `(state_number, index)` so
8946            // the inline-consumed tokens belong to *this* call's output, not
8947            // the cached result. Memoize the bare outcomes (without the
8948            // inline-pending data), then prepend the inline data on return.
8949            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
8950            memo.insert(key, Rc::clone(&stored));
8951            if inline_pending {
8952                return stored
8953                    .iter()
8954                    .copied()
8955                    .map(&mut apply_inline_pending)
8956                    .collect();
8957            }
8958            return stored.to_vec();
8959        }
8960        #[cfg(feature = "perf-counters")]
8961        match outcomes.len() {
8962            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8963            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8964            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8965        }
8966        if inline_pending {
8967            return outcomes.into_iter().map(apply_inline_pending).collect();
8968        }
8969        outcomes
8970    }
8971
8972    /// Explores single-token deletion recovery while preserving the matched
8973    /// token and skipped error token in the selected parse tree path.
8974    fn single_token_deletion_recovery(
8975        &mut self,
8976        recovery: RecoveryRequest<'_, '_>,
8977    ) -> Vec<RecognizeOutcome> {
8978        let RecoveryRequest {
8979            atn,
8980            transition,
8981            expected_symbols,
8982            target,
8983            request,
8984            visiting,
8985            memo,
8986            expected,
8987        } = recovery;
8988        let RecognizeRequest {
8989            stop_state,
8990            index,
8991            rule_start_index,
8992            decision_start_index,
8993            init_action_rules,
8994            predicates,
8995            semantics,
8996            rule_args,
8997            member_actions,
8998            return_actions,
8999            local_int_arg,
9000            member_values,
9001            return_values,
9002            rule_alt_number,
9003            track_alt_numbers,
9004            consumed_eof,
9005            precedence,
9006            depth,
9007            ..
9008        } = request;
9009        let Some((diagnostic, next_index, next_symbol)) =
9010            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
9011        else {
9012            return Vec::new();
9013        };
9014        let after_next = self.consume_index(next_index, next_symbol);
9015        self.recognize_state(
9016            atn,
9017            RecognizeRequest {
9018                state_number: target,
9019                stop_state,
9020                index: after_next,
9021                rule_start_index,
9022                decision_start_index,
9023                init_action_rules,
9024                predicates,
9025                semantics,
9026                rule_args,
9027                member_actions,
9028                return_actions,
9029                local_int_arg,
9030                member_values,
9031                return_values,
9032                rule_alt_number,
9033                track_alt_numbers,
9034                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
9035                precedence,
9036                depth: depth + 1,
9037                recovery_symbols: BTreeSet::new(),
9038                recovery_state: None,
9039            },
9040            visiting,
9041            memo,
9042            expected,
9043        )
9044        .into_iter()
9045        .map(|mut outcome| {
9046            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9047            outcome.diagnostics = self
9048                .recognition_arena
9049                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9050            let token = self.arena_token_node(next_index, false);
9051            self.arena_prepend(&mut outcome.nodes, token);
9052            let error = self.arena_token_node(index, true);
9053            self.arena_prepend(&mut outcome.nodes, error);
9054            outcome
9055        })
9056        .collect()
9057    }
9058
9059    /// Retries the current recognition state after deleting one unexpected
9060    /// token, preserving the deleted token as an error node in the parse tree.
9061    fn current_token_deletion_recovery(
9062        &mut self,
9063        recovery: CurrentTokenDeletionRequest<'_, '_>,
9064    ) -> Vec<RecognizeOutcome> {
9065        let CurrentTokenDeletionRequest {
9066            atn,
9067            expected_symbols,
9068            mut request,
9069            visiting,
9070            memo,
9071            expected,
9072        } = recovery;
9073        let error_index = request.index;
9074        if error_index == request.rule_start_index {
9075            return Vec::new();
9076        }
9077        let Some((diagnostic, next_index, skipped)) =
9078            self.current_token_deletion(error_index, &expected_symbols)
9079        else {
9080            return Vec::new();
9081        };
9082        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9083        request.index = next_index;
9084        request.depth += 1;
9085        request.recovery_state = None;
9086        self.recognize_state(atn, request, visiting, memo, expected)
9087            .into_iter()
9088            .map(|mut outcome| {
9089                outcome.diagnostics = self
9090                    .recognition_arena
9091                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9092                for index in skipped.iter().rev() {
9093                    let error = self.arena_token_node(*index, true);
9094                    self.arena_prepend(&mut outcome.nodes, error);
9095                }
9096                outcome
9097            })
9098            .collect()
9099    }
9100
9101    /// Falls back after deletion/insertion repairs cannot continue from a
9102    /// failed consuming transition.
9103    fn consuming_failure_fallback(
9104        &mut self,
9105        fallback: ConsumingFailureFallback<'_>,
9106        visiting: &mut BTreeSet<RecognizeKey>,
9107        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9108        expected: &mut ExpectedTokens,
9109    ) -> Vec<RecognizeOutcome> {
9110        if fallback.expected_symbols.is_empty() {
9111            return Vec::new();
9112        }
9113        if fallback.symbol == TOKEN_EOF {
9114            return self.eof_consuming_failure_fallback(fallback, expected);
9115        }
9116        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9117    }
9118
9119    /// Keeps unexpected non-EOF input visible as an error node when no repair
9120    /// path can otherwise reach the transition target.
9121    fn non_eof_consuming_failure_fallback(
9122        &mut self,
9123        fallback: ConsumingFailureFallback<'_>,
9124        visiting: &mut BTreeSet<RecognizeKey>,
9125        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9126        expected: &mut ExpectedTokens,
9127    ) -> Vec<RecognizeOutcome> {
9128        let ConsumingFailureFallback {
9129            atn,
9130            target,
9131            request,
9132            symbol,
9133            expected_symbols,
9134            decision_start_index,
9135            decision,
9136        } = fallback;
9137        let error_index = request.index;
9138        let diagnostic =
9139            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
9140        let next_index = self.consume_index(error_index, symbol);
9141        self.recognize_state(
9142            atn,
9143            RecognizeRequest {
9144                state_number: target,
9145                stop_state: request.stop_state,
9146                index: next_index,
9147                rule_start_index: request.rule_start_index,
9148                decision_start_index,
9149                init_action_rules: request.init_action_rules,
9150                predicates: request.predicates,
9151                semantics: request.semantics,
9152                rule_args: request.rule_args,
9153                member_actions: request.member_actions,
9154                return_actions: request.return_actions,
9155                local_int_arg: request.local_int_arg,
9156                member_values: request.member_values,
9157                return_values: request.return_values,
9158                rule_alt_number: request.rule_alt_number,
9159                track_alt_numbers: request.track_alt_numbers,
9160                consumed_eof: request.consumed_eof,
9161                precedence: request.precedence,
9162                depth: request.depth + 1,
9163                recovery_symbols: BTreeSet::new(),
9164                recovery_state: None,
9165            },
9166            visiting,
9167            memo,
9168            expected,
9169        )
9170        .into_iter()
9171        .map(|mut outcome| {
9172            prepend_decision(&mut outcome, decision);
9173            outcome.diagnostics = self
9174                .recognition_arena
9175                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9176            let error = self.arena_token_node(error_index, true);
9177            self.arena_prepend(&mut outcome.nodes, error);
9178            outcome
9179        })
9180        .collect()
9181    }
9182
9183    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
9184    /// behavior of unwinding instead of inserting caller tokens at EOF.
9185    fn eof_consuming_failure_fallback(
9186        &mut self,
9187        fallback: ConsumingFailureFallback<'_>,
9188        expected: &ExpectedTokens,
9189    ) -> Vec<RecognizeOutcome> {
9190        let request = fallback.request;
9191        if request.index == request.rule_start_index {
9192            return Vec::new();
9193        }
9194        let diagnostic =
9195            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
9196        let diagnostics = self
9197            .recognition_arena
9198            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
9199        vec![RecognizeOutcome {
9200            index: request.index,
9201            consumed_eof: request.consumed_eof,
9202            alt_number: request.rule_alt_number,
9203            member_values: request.member_values,
9204            return_values: request.return_values,
9205            diagnostics,
9206            decisions: Vec::new(),
9207            actions: Vec::new(),
9208            nodes: NodeSeqId::EMPTY,
9209        }]
9210    }
9211
9212    /// Explores single-token insertion recovery while adding a conjured
9213    /// missing-token error node to the selected parse tree path.
9214    fn single_token_insertion_recovery(
9215        &mut self,
9216        recovery: RecoveryRequest<'_, '_>,
9217    ) -> Vec<RecognizeOutcome> {
9218        let RecoveryRequest {
9219            atn,
9220            transition,
9221            expected_symbols,
9222            target,
9223            request,
9224            visiting,
9225            memo,
9226            expected,
9227        } = recovery;
9228        let RecognizeRequest {
9229            stop_state,
9230            index,
9231            rule_start_index,
9232            decision_start_index,
9233            init_action_rules,
9234            predicates,
9235            semantics,
9236            rule_args,
9237            member_actions,
9238            return_actions,
9239            local_int_arg,
9240            member_values,
9241            return_values,
9242            rule_alt_number,
9243            track_alt_numbers,
9244            consumed_eof,
9245            precedence,
9246            depth,
9247            ..
9248        } = request;
9249        let follow_symbols = state_expected_symbols(atn, transition.target());
9250        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
9251            transition,
9252            index,
9253            atn.max_token_type(),
9254            &expected_symbols,
9255            &follow_symbols,
9256        ) else {
9257            return Vec::new();
9258        };
9259        self.recognize_state(
9260            atn,
9261            RecognizeRequest {
9262                state_number: target,
9263                stop_state,
9264                index,
9265                rule_start_index,
9266                decision_start_index,
9267                init_action_rules,
9268                predicates,
9269                semantics,
9270                rule_args,
9271                member_actions,
9272                return_actions,
9273                local_int_arg,
9274                member_values,
9275                return_values,
9276                rule_alt_number,
9277                track_alt_numbers,
9278                consumed_eof,
9279                precedence,
9280                depth: depth + 1,
9281                recovery_symbols: BTreeSet::new(),
9282                recovery_state: None,
9283            },
9284            visiting,
9285            memo,
9286            expected,
9287        )
9288        .into_iter()
9289        .map(|mut outcome| {
9290            outcome.diagnostics = self
9291                .recognition_arena
9292                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9293            let missing = self.arena_missing_token_node(token_type, index, text.clone());
9294            self.arena_prepend(&mut outcome.nodes, missing);
9295            outcome
9296        })
9297        .collect()
9298    }
9299
9300    /// Attempts to reach `stop_state` and carries semantic actions for the
9301    /// selected parser path.
9302    #[allow(clippy::too_many_lines)]
9303    fn recognize_state(
9304        &mut self,
9305        atn: &Atn,
9306        request: RecognizeRequest<'_>,
9307        visiting: &mut BTreeSet<RecognizeKey>,
9308        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9309        expected: &mut ExpectedTokens,
9310    ) -> Vec<RecognizeOutcome> {
9311        let request_template = request.clone();
9312        let RecognizeRequest {
9313            state_number,
9314            stop_state,
9315            index,
9316            rule_start_index,
9317            decision_start_index,
9318            init_action_rules,
9319            predicates,
9320            semantics,
9321            rule_args,
9322            member_actions,
9323            return_actions,
9324            local_int_arg,
9325            member_values,
9326            return_values,
9327            rule_alt_number,
9328            track_alt_numbers,
9329            consumed_eof,
9330            precedence,
9331            depth,
9332            recovery_symbols,
9333            recovery_state,
9334        } = request;
9335        if depth > RECOGNITION_DEPTH_LIMIT {
9336            return Vec::new();
9337        }
9338        if state_number == stop_state {
9339            return stop_outcome(
9340                index,
9341                consumed_eof,
9342                rule_alt_number,
9343                member_values,
9344                return_values,
9345            );
9346        }
9347        let key = RecognizeKey {
9348            state_number,
9349            stop_state,
9350            index,
9351            rule_start_index,
9352            decision_start_index,
9353            local_int_arg,
9354            member_values: member_values.clone(),
9355            return_values: return_values.clone(),
9356            rule_alt_number,
9357            track_alt_numbers,
9358            consumed_eof,
9359            precedence,
9360            recovery_symbols: recovery_symbols.clone(),
9361            recovery_state,
9362        };
9363        if let Some(outcomes) = memo.get(&key) {
9364            return outcomes.clone();
9365        }
9366
9367        let visit_key = key.clone();
9368        if !visiting.insert(visit_key.clone()) {
9369            return Vec::new();
9370        }
9371
9372        let Some(state) = atn.state(state_number) else {
9373            visiting.remove(&visit_key);
9374            return Vec::new();
9375        };
9376        let transitions = state.transitions();
9377        let transition_count = transitions.len();
9378        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9379            Some(index)
9380        } else {
9381            decision_start_index
9382        };
9383        let (epsilon_recovery_symbols, epsilon_recovery_state) =
9384            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
9385        let mut outcomes = Vec::new();
9386        for (transition_index, transition) in transitions.iter().enumerate() {
9387            let decision =
9388                transition_decision(atn, state, transition_count, transition_index, predicates);
9389            let next_alt_number = next_alt_number(
9390                state,
9391                transition_count,
9392                transition_index,
9393                rule_alt_number,
9394                track_alt_numbers,
9395            );
9396            let transition_data = transition.data();
9397            match &transition_data {
9398                Transition::Epsilon { target } | Transition::Action { target, .. } => {
9399                    let action_rule_index = match &transition_data {
9400                        Transition::Action { rule_index, .. } => Some(*rule_index),
9401                        _ => None,
9402                    };
9403                    outcomes.extend(self.recognize_epsilon_or_action_step(
9404                        atn,
9405                        &request_template,
9406                        EpsilonActionStep {
9407                            source_state: state_number,
9408                            target: *target,
9409                            action_rule_index,
9410                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
9411                            decision,
9412                            decision_start_index: next_decision_start_index,
9413                            alt_number: next_alt_number,
9414                            recovery_symbols: epsilon_recovery_symbols.clone(),
9415                            recovery_state: epsilon_recovery_state,
9416                        },
9417                        RecognizeScratch {
9418                            visiting,
9419                            memo,
9420                            expected,
9421                        },
9422                    ));
9423                }
9424                Transition::Predicate {
9425                    target,
9426                    rule_index,
9427                    pred_index,
9428                    ..
9429                } => {
9430                    let predicate = PredicateEval {
9431                        index,
9432                        rule_index: *rule_index,
9433                        pred_index: *pred_index,
9434                        predicates,
9435                        semantics,
9436                        context: None,
9437                        local_int_arg,
9438                        member_values: &member_values,
9439                    };
9440                    if self.parser_predicate_matches(predicate) {
9441                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
9442                        outcomes.extend(
9443                            self.recognize_state(
9444                                atn,
9445                                RecognizeRequest {
9446                                    state_number: *target,
9447                                    stop_state,
9448                                    index,
9449                                    rule_start_index,
9450                                    decision_start_index: next_decision_start_index,
9451                                    init_action_rules,
9452                                    predicates,
9453                                    semantics,
9454                                    rule_args,
9455                                    member_actions,
9456                                    return_actions,
9457                                    local_int_arg,
9458                                    member_values: member_values.clone(),
9459                                    return_values: return_values.clone(),
9460                                    rule_alt_number: next_alt_number,
9461                                    track_alt_numbers,
9462                                    consumed_eof,
9463                                    precedence,
9464                                    depth: depth + 1,
9465                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9466                                    recovery_state: epsilon_recovery_state,
9467                                },
9468                                visiting,
9469                                memo,
9470                                expected,
9471                            )
9472                            .into_iter()
9473                            .map(|mut outcome| {
9474                                prepend_decision(&mut outcome, decision);
9475                                if let Some(rule_index) = left_recursive_boundary {
9476                                    let boundary = self.arena_boundary_node(rule_index);
9477                                    self.arena_prepend(&mut outcome.nodes, boundary);
9478                                }
9479                                outcome
9480                            }),
9481                        );
9482                    } else if let Some(message) = semantics
9483                        .and_then(|semantics| {
9484                            self.parser_semantic_ir_predicate_failure_message(
9485                                *rule_index,
9486                                *pred_index,
9487                                semantics,
9488                            )
9489                        })
9490                        .or_else(|| {
9491                            self.parser_predicate_failure_message(
9492                                *rule_index,
9493                                *pred_index,
9494                                predicates,
9495                            )
9496                        })
9497                    {
9498                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
9499                            rule_index: *rule_index,
9500                            index,
9501                            message,
9502                            member_values: member_values.clone(),
9503                            return_values: return_values.clone(),
9504                            rule_alt_number,
9505                        }));
9506                    } else {
9507                        record_predicate_no_viable(expected, next_decision_start_index, index);
9508                    }
9509                }
9510                Transition::Precedence {
9511                    target,
9512                    precedence: transition_precedence,
9513                } => {
9514                    if *transition_precedence >= precedence {
9515                        outcomes.extend(
9516                            self.recognize_state(
9517                                atn,
9518                                RecognizeRequest {
9519                                    state_number: *target,
9520                                    stop_state,
9521                                    index,
9522                                    rule_start_index,
9523                                    decision_start_index: next_decision_start_index,
9524                                    init_action_rules,
9525                                    predicates,
9526                                    semantics,
9527                                    rule_args,
9528                                    member_actions,
9529                                    return_actions,
9530                                    local_int_arg,
9531                                    member_values: member_values.clone(),
9532                                    return_values: return_values.clone(),
9533                                    rule_alt_number: next_alt_number,
9534                                    track_alt_numbers,
9535                                    consumed_eof,
9536                                    precedence,
9537                                    depth: depth + 1,
9538                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9539                                    recovery_state: epsilon_recovery_state,
9540                                },
9541                                visiting,
9542                                memo,
9543                                expected,
9544                            )
9545                            .into_iter()
9546                            .map(|mut outcome| {
9547                                prepend_decision(&mut outcome, decision);
9548                                outcome
9549                            }),
9550                        );
9551                    }
9552                }
9553                Transition::Rule {
9554                    target,
9555                    rule_index,
9556                    follow_state,
9557                    precedence: rule_precedence,
9558                    ..
9559                } => {
9560                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
9561                        continue;
9562                    };
9563                    let child_local_int_arg =
9564                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
9565                    let expected_before_child = expected.clone();
9566                    let children = self.recognize_state(
9567                        atn,
9568                        RecognizeRequest {
9569                            state_number: *target,
9570                            stop_state: child_stop,
9571                            index,
9572                            rule_start_index: index,
9573                            decision_start_index: None,
9574                            init_action_rules,
9575                            predicates,
9576                            semantics,
9577                            rule_args,
9578                            member_actions,
9579                            return_actions,
9580                            local_int_arg: child_local_int_arg,
9581                            member_values: member_values.clone(),
9582                            return_values: BTreeMap::new(),
9583                            rule_alt_number: 0,
9584                            track_alt_numbers,
9585                            consumed_eof: false,
9586                            precedence: *rule_precedence,
9587                            depth: depth + 1,
9588                            recovery_symbols: epsilon_recovery_symbols.clone(),
9589                            recovery_state: epsilon_recovery_state,
9590                        },
9591                        visiting,
9592                        memo,
9593                        expected,
9594                    );
9595                    let children = if children.is_empty() {
9596                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
9597                            atn,
9598                            rule_index: *rule_index,
9599                            start_index: index,
9600                            follow_state: *follow_state,
9601                            stop_state,
9602                            member_values: member_values.clone(),
9603                            expected,
9604                        })
9605                    } else {
9606                        children
9607                    };
9608                    let preserve_child_expected =
9609                        self.child_expected_reaches_clean_eof(&children, expected);
9610                    restore_expected(
9611                        &children,
9612                        index,
9613                        expected,
9614                        expected_before_child,
9615                        preserve_child_expected,
9616                    );
9617                    for child in children {
9618                        let child_stop_index =
9619                            self.rule_stop_token_index(child.index, child.consumed_eof);
9620                        let child_nodes = self
9621                            .recognition_arena
9622                            .fold_left_recursive_boundaries(child.nodes);
9623                        let child_node = self.arena_rule_node(ArenaRuleSpec {
9624                            rule_index: *rule_index,
9625                            invoking_state: invoking_state_number(state_number),
9626                            alt_number: child.alt_number,
9627                            start_index: index,
9628                            stop_index: child_stop_index,
9629                            return_values: child.return_values.clone(),
9630                            children: child_nodes,
9631                        });
9632                        outcomes.extend(
9633                            self.recognize_state(
9634                                atn,
9635                                RecognizeRequest {
9636                                    state_number: *follow_state,
9637                                    stop_state,
9638                                    index: child.index,
9639                                    rule_start_index,
9640                                    decision_start_index: next_decision_start_index,
9641                                    init_action_rules,
9642                                    predicates,
9643                                    semantics,
9644                                    rule_args,
9645                                    member_actions,
9646                                    return_actions,
9647                                    local_int_arg,
9648                                    member_values: child.member_values.clone(),
9649                                    return_values: return_values.clone(),
9650                                    rule_alt_number,
9651                                    track_alt_numbers,
9652                                    consumed_eof: consumed_eof || child.consumed_eof,
9653                                    precedence,
9654                                    depth: depth + 1,
9655                                    recovery_symbols: BTreeSet::new(),
9656                                    recovery_state: None,
9657                                },
9658                                visiting,
9659                                memo,
9660                                expected,
9661                            )
9662                            .into_iter()
9663                            .map(|mut outcome| {
9664                                outcome.consumed_eof |= child.consumed_eof;
9665                                outcome.diagnostics = self
9666                                    .recognition_arena
9667                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
9668                                let mut decisions = child.decisions.clone();
9669                                decisions.append(&mut outcome.decisions);
9670                                outcome.decisions = decisions;
9671                                prepend_decision(&mut outcome, decision);
9672                                let mut actions = child.actions.clone();
9673                                if init_action_rules.contains(rule_index) {
9674                                    actions.insert(
9675                                        0,
9676                                        ParserAction::new_rule_init(
9677                                            *rule_index,
9678                                            index,
9679                                            Some(*follow_state),
9680                                        ),
9681                                    );
9682                                }
9683                                actions.append(&mut outcome.actions);
9684                                outcome.actions = actions;
9685                                self.arena_prepend(&mut outcome.nodes, child_node);
9686                                outcome
9687                            }),
9688                        );
9689                    }
9690                }
9691                Transition::Atom { target, .. }
9692                | Transition::Range { target, .. }
9693                | Transition::Set { target, .. }
9694                | Transition::NotSet { target, .. }
9695                | Transition::Wildcard { target, .. } => {
9696                    let symbol = self.token_type_at(index);
9697                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
9698                        let next_index = self.consume_index(index, symbol);
9699                        outcomes.extend(
9700                            self.recognize_state(
9701                                atn,
9702                                RecognizeRequest {
9703                                    state_number: *target,
9704                                    stop_state,
9705                                    index: next_index,
9706                                    rule_start_index,
9707                                    decision_start_index: next_decision_start_index,
9708                                    init_action_rules,
9709                                    predicates,
9710                                    semantics,
9711                                    rule_args,
9712                                    member_actions,
9713                                    return_actions,
9714                                    local_int_arg,
9715                                    member_values: member_values.clone(),
9716                                    return_values: return_values.clone(),
9717                                    rule_alt_number: next_alt_number,
9718                                    track_alt_numbers,
9719                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
9720                                    precedence,
9721                                    depth: depth + 1,
9722                                    recovery_symbols: BTreeSet::new(),
9723                                    recovery_state: None,
9724                                },
9725                                visiting,
9726                                memo,
9727                                expected,
9728                            )
9729                            .into_iter()
9730                            .map(|mut outcome| {
9731                                prepend_decision(&mut outcome, decision);
9732                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9733                                let token = self.arena_token_node(index, false);
9734                                self.arena_prepend(&mut outcome.nodes, token);
9735                                outcome
9736                            }),
9737                        );
9738                    } else {
9739                        let expected_symbols =
9740                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
9741                        if expected_symbols.contains(&symbol) {
9742                            continue;
9743                        }
9744                        expected.record_transition(index, transition, atn.max_token_type());
9745                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
9746                        let before_recovery = outcomes.len();
9747                        let recovery_request = request_template.clone();
9748                        outcomes.extend(
9749                            self.single_token_deletion_recovery(RecoveryRequest {
9750                                atn,
9751                                transition,
9752                                expected_symbols: expected_symbols.clone(),
9753                                target: *target,
9754                                request: recovery_request.clone(),
9755                                visiting,
9756                                memo,
9757                                expected,
9758                            })
9759                            .into_iter()
9760                            .map(|mut outcome| {
9761                                prepend_decision(&mut outcome, decision);
9762                                outcome
9763                            }),
9764                        );
9765                        if !state_is_left_recursive_rule(atn, state) {
9766                            outcomes.extend(
9767                                self.single_token_insertion_recovery(RecoveryRequest {
9768                                    atn,
9769                                    transition,
9770                                    expected_symbols: expected_symbols.clone(),
9771                                    target: *target,
9772                                    request: recovery_request.clone(),
9773                                    visiting,
9774                                    memo,
9775                                    expected,
9776                                })
9777                                .into_iter()
9778                                .map(|mut outcome| {
9779                                    prepend_decision(&mut outcome, decision);
9780                                    outcome
9781                                }),
9782                            );
9783                        }
9784                        outcomes.extend(self.current_token_deletion_recovery(
9785                            CurrentTokenDeletionRequest {
9786                                atn,
9787                                expected_symbols: expected_symbols.clone(),
9788                                request: recovery_request.clone(),
9789                                visiting,
9790                                memo,
9791                                expected,
9792                            },
9793                        ));
9794                        if outcomes.len() == before_recovery {
9795                            outcomes.extend(self.consuming_failure_fallback(
9796                                ConsumingFailureFallback {
9797                                    atn,
9798                                    target: *target,
9799                                    request: recovery_request,
9800                                    symbol,
9801                                    expected_symbols,
9802                                    decision_start_index: next_decision_start_index,
9803                                    decision,
9804                                },
9805                                visiting,
9806                                memo,
9807                                expected,
9808                            ));
9809                        }
9810                    }
9811                }
9812            }
9813        }
9814
9815        visiting.remove(&visit_key);
9816        self.record_prediction_diagnostics(atn, state, index, &outcomes);
9817        if matches!(
9818            self.prediction_mode,
9819            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9820        ) {
9821            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
9822        }
9823        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
9824        memo.insert(key, outcomes.clone());
9825        outcomes
9826    }
9827
9828    /// Follows an epsilon or semantic-action transition while preserving the
9829    /// path-local side effects that may later become generated action output.
9830    fn recognize_epsilon_or_action_step(
9831        &mut self,
9832        atn: &Atn,
9833        request: &RecognizeRequest<'_>,
9834        step: EpsilonActionStep,
9835        scratch: RecognizeScratch<'_>,
9836    ) -> Vec<RecognizeOutcome> {
9837        let RecognizeScratch {
9838            visiting,
9839            memo,
9840            expected,
9841        } = scratch;
9842        let action = step.action_rule_index.map(|rule_index| {
9843            ParserAction::new(
9844                step.source_state,
9845                rule_index,
9846                request.rule_start_index,
9847                self.rule_stop_token_index(request.index, request.consumed_eof),
9848            )
9849        });
9850        let next_member_values = if action.is_some() {
9851            member_values_after_action(
9852                step.source_state,
9853                request.member_actions,
9854                request.semantics,
9855                &request.member_values,
9856            )
9857        } else {
9858            request.member_values.clone()
9859        };
9860        let next_return_values = action.map_or_else(
9861            || request.return_values.clone(),
9862            |action| {
9863                return_values_after_action(
9864                    step.source_state,
9865                    action.rule_index(),
9866                    request.return_actions,
9867                    request.semantics,
9868                    &request.return_values,
9869                )
9870            },
9871        );
9872
9873        self.recognize_state(
9874            atn,
9875            RecognizeRequest {
9876                state_number: step.target,
9877                stop_state: request.stop_state,
9878                index: request.index,
9879                rule_start_index: request.rule_start_index,
9880                decision_start_index: step.decision_start_index,
9881                init_action_rules: request.init_action_rules,
9882                predicates: request.predicates,
9883                semantics: request.semantics,
9884                rule_args: request.rule_args,
9885                member_actions: request.member_actions,
9886                return_actions: request.return_actions,
9887                local_int_arg: request.local_int_arg,
9888                member_values: next_member_values,
9889                return_values: next_return_values,
9890                rule_alt_number: step.alt_number,
9891                track_alt_numbers: request.track_alt_numbers,
9892                consumed_eof: request.consumed_eof,
9893                precedence: request.precedence,
9894                depth: request.depth + 1,
9895                recovery_symbols: step.recovery_symbols,
9896                recovery_state: step.recovery_state,
9897            },
9898            visiting,
9899            memo,
9900            expected,
9901        )
9902        .into_iter()
9903        .map(|mut outcome| {
9904            prepend_decision(&mut outcome, step.decision);
9905            if let Some(rule_index) = step.left_recursive_boundary {
9906                let boundary = self.arena_boundary_node(rule_index);
9907                self.arena_prepend(&mut outcome.nodes, boundary);
9908            }
9909            if let Some(action) = action {
9910                outcome.actions.insert(0, action);
9911            }
9912            outcome
9913        })
9914        .collect()
9915    }
9916
9917    /// Reads the token type at an absolute token-stream index without moving
9918    /// the parser's stream cursor. The fast recognizer probes lookahead at
9919    /// every state visit, so avoiding the seek round-trip is a measurable
9920    /// hot-path win on long inputs.
9921    fn token_type_at(&mut self, index: usize) -> i32 {
9922        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
9923            self.input.fill();
9924        }
9925        self.input.token_type_at_index(index)
9926    }
9927
9928    /// Returns the cached `state_expected_symbols` set for an ATN state.
9929    ///
9930    /// The fast recognizer consults this set on every state visit through
9931    /// `next_recovery_context`; the underlying DFS is a pure function of the
9932    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
9933    ///
9934    /// Caching is layered through `intern_recovery_symbols` so two ATN states
9935    /// with the same expected-symbol set share one `Rc`. That invariant is
9936    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
9937    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
9938    /// always interned before it ends up in a key.
9939    fn cached_state_expected_symbols(
9940        &mut self,
9941        atn: &Atn,
9942        state_number: usize,
9943    ) -> Rc<BTreeSet<i32>> {
9944        if let Some(cached) = self.state_expected_cache.get(&state_number) {
9945            return Rc::clone(cached);
9946        }
9947        let symbols = state_expected_symbols(atn, state_number);
9948        let entry = self.intern_recovery_symbols(symbols);
9949        self.state_expected_cache
9950            .insert(state_number, Rc::clone(&entry));
9951        entry
9952    }
9953
9954    fn cached_state_expected_token_set(
9955        &mut self,
9956        atn: &Atn,
9957        state_number: usize,
9958    ) -> Rc<TokenBitSet> {
9959        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
9960            return Rc::clone(cached);
9961        }
9962        // Purely a function of the ATN, so back the per-parser cache with the
9963        // thread-shared one — fresh parser instances (one per parse in
9964        // generated usage) start warm instead of rewalking the ATN.
9965        let symbols = with_shared_atn_caches(atn, |cache| {
9966            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
9967                return Rc::clone(cached);
9968            }
9969            let symbols = Rc::new(state_expected_token_set(atn, state_number));
9970            cache
9971                .state_expected_tokens
9972                .insert(state_number, Rc::clone(&symbols));
9973            symbols
9974        });
9975        self.state_expected_token_cache
9976            .insert(state_number, Rc::clone(&symbols));
9977        symbols
9978    }
9979
9980    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
9981        if self.rule_stop_reach_cache.len() <= state_number {
9982            self.rule_stop_reach_cache
9983                .resize_with(atn.states().len().max(state_number + 1), || None);
9984        }
9985        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
9986            return reaches;
9987        }
9988        let reaches = with_shared_atn_caches(atn, |cache| {
9989            *cache
9990                .rule_stop_reach
9991                .entry(state_number)
9992                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
9993        });
9994        self.rule_stop_reach_cache[state_number] = Some(reaches);
9995        reaches
9996    }
9997
9998    /// Returns the parser's empty `recovery_symbols` singleton so callers can
9999    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
10000    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
10001        Rc::clone(&self.empty_recovery_symbols)
10002    }
10003
10004    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
10005    /// recognizer can hash and compare keys by pointer.
10006    ///
10007    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
10008    /// come from this method or the empty singleton; otherwise two
10009    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
10010    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
10011    /// recognition coordinates.
10012    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
10013        if set.is_empty() {
10014            return Rc::clone(&self.empty_recovery_symbols);
10015        }
10016        let candidate = Rc::new(set);
10017        match self.recovery_symbols_intern.get(&candidate) {
10018            Some(existing) => Rc::clone(existing),
10019            None => {
10020                self.recovery_symbols_intern
10021                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
10022                candidate
10023            }
10024        }
10025    }
10026
10027    /// Returns the cached look-1 entry for a decision state, computing it on
10028    /// first use. Multi-alternative states are visited many times during
10029    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
10030    /// hash lookup per visit.
10031    fn cached_decision_lookahead(
10032        &mut self,
10033        atn: &Atn,
10034        state: AtnState<'_>,
10035        rule_stop_state: usize,
10036    ) -> Rc<DecisionLookahead> {
10037        // Hit the parser-instance cache first. Decision lookahead is purely
10038        // a function of the ATN/state, so on a warm cache we skip the
10039        // thread-local + RefCell + HashMap-entry dance through
10040        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
10041        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
10042        // hashmap-entry overhead in profiles.
10043        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
10044            return Rc::clone(cached);
10045        }
10046        let entry = with_shared_atn_caches(atn, |cache| {
10047            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10048                return Rc::clone(cached);
10049            }
10050            let mut entry = DecisionLookahead {
10051                transitions: Vec::with_capacity(state.transitions().len()),
10052            };
10053            for transition in &state.transitions() {
10054                entry.transitions.push(transition_first_set(
10055                    atn,
10056                    transition,
10057                    rule_stop_state,
10058                    &mut cache.first_set,
10059                ));
10060            }
10061            let entry = Rc::new(entry);
10062            cache
10063                .decision_lookahead
10064                .insert(state.state_number(), Rc::clone(&entry));
10065            entry
10066        });
10067        self.decision_lookahead_cache
10068            .insert(state.state_number(), Rc::clone(&entry));
10069        entry
10070    }
10071
10072    fn cached_rule_first_set(
10073        &mut self,
10074        atn: &Atn,
10075        target: usize,
10076        child_stop: usize,
10077    ) -> Rc<FirstSet> {
10078        if self.rule_first_set_cache.len() <= target {
10079            self.rule_first_set_cache
10080                .resize_with(atn.states().len().max(target + 1), || None);
10081        }
10082        if let Some(cached) = self
10083            .rule_first_set_cache
10084            .get(target)
10085            .and_then(Option::as_ref)
10086        {
10087            return Rc::clone(cached);
10088        }
10089        let first = with_shared_first_set_cache(atn, |cache| {
10090            rule_first_set(atn, target, child_stop, cache)
10091        });
10092        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
10093        first
10094    }
10095
10096    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
10097        let atn_key = SharedAtnCacheKey::for_atn(atn);
10098        if self.empty_cycle_cache_atn != Some(atn_key) {
10099            self.empty_cycle_cache.clear();
10100            self.empty_cycle_cache_atn = Some(atn_key);
10101        }
10102        if self.empty_cycle_cache.len() <= state_number {
10103            self.empty_cycle_cache
10104                .resize_with(atn.state_count().max(state_number + 1), || None);
10105        }
10106        if let Some(cached) = self.empty_cycle_cache[state_number] {
10107            return cached;
10108        }
10109        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
10110        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
10111        self.empty_cycle_cache[state_number] = Some(result);
10112        result
10113    }
10114
10115    fn empty_path_reaches_state(
10116        &mut self,
10117        atn: &Atn,
10118        state_number: usize,
10119        target_state: usize,
10120        visited: &mut FxHashSet<usize>,
10121    ) -> bool {
10122        enum Work {
10123            Visit(usize),
10124            RuleFollow {
10125                target: usize,
10126                rule_index: usize,
10127                follow_state: usize,
10128            },
10129        }
10130
10131        let mut work = vec![Work::Visit(state_number)];
10132        while let Some(item) = work.pop() {
10133            match item {
10134                Work::Visit(state_number) => {
10135                    if !visited.insert(state_number) {
10136                        continue;
10137                    }
10138                    let Some(state) = atn.state(state_number) else {
10139                        continue;
10140                    };
10141                    let transitions = state.transitions();
10142                    for transition_index in (0..transitions.len()).rev() {
10143                        let transition = transitions
10144                            .get(transition_index)
10145                            .expect("in-bounds parser transition");
10146                        let kind = transition.kind();
10147                        let target = transition.target();
10148                        match kind {
10149                            ParserTransitionKind::Atom
10150                            | ParserTransitionKind::Range
10151                            | ParserTransitionKind::Set
10152                            | ParserTransitionKind::NotSet
10153                            | ParserTransitionKind::Wildcard => {}
10154                            ParserTransitionKind::Rule => {
10155                                if target == target_state {
10156                                    return true;
10157                                }
10158                                work.push(Work::RuleFollow {
10159                                    target,
10160                                    rule_index: transition.arg0() as usize,
10161                                    follow_state: transition.arg1() as usize,
10162                                });
10163                                work.push(Work::Visit(target));
10164                            }
10165                            ParserTransitionKind::Epsilon
10166                            | ParserTransitionKind::Predicate
10167                            | ParserTransitionKind::Action
10168                            | ParserTransitionKind::Precedence => {
10169                                if target == target_state {
10170                                    return true;
10171                                }
10172                                work.push(Work::Visit(target));
10173                            }
10174                        }
10175                    }
10176                }
10177                Work::RuleFollow {
10178                    target,
10179                    rule_index,
10180                    follow_state,
10181                } => {
10182                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
10183                        continue;
10184                    };
10185                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
10186                        if follow_state == target_state {
10187                            return true;
10188                        }
10189                        work.push(Work::Visit(follow_state));
10190                    }
10191                }
10192            }
10193        }
10194        false
10195    }
10196
10197    /// Decides whether the clean recognizer should use its full outcome memo
10198    /// table for this coordinate.
10199    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
10200        match self.clean_memo_mode {
10201            CleanMemoMode::Promote => true,
10202            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
10203            CleanMemoMode::Sparse => {
10204                self.clean_memo_sparse_samples += 1;
10205                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
10206                    return false;
10207                }
10208                self.clean_memo_sparse_samples = 0;
10209                self.clean_memo_mode = CleanMemoMode::Probe;
10210                self.clean_memo_probe_samples = 0;
10211                self.clean_memo_probe_repeats = 0;
10212                self.clean_memo_probe_seen.clear();
10213                self.observe_clean_memo_probe(key)
10214            }
10215        }
10216    }
10217
10218    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
10219        self.clean_memo_probe_samples += 1;
10220        if !self.clean_memo_probe_seen.insert(key.clone()) {
10221            self.clean_memo_probe_repeats += 1;
10222        }
10223        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
10224            self.clean_memo_mode = CleanMemoMode::Promote;
10225            self.clean_memo_probe_seen.clear();
10226            return true;
10227        }
10228        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
10229            self.clean_memo_mode = CleanMemoMode::Sparse;
10230            self.clean_memo_sparse_samples = 0;
10231            self.clean_memo_probe_seen.clear();
10232            return false;
10233        }
10234        true
10235    }
10236
10237    /// Borrows the visible token at an absolute token-stream index.
10238    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
10239        self.input.get(index)
10240    }
10241
10242    /// Returns the compact token ID at an absolute token-stream index.
10243    fn token_id_at(&self, index: usize) -> Option<TokenId> {
10244        self.input.get_id(index)
10245    }
10246
10247    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
10248        let token = self
10249            .token_id_at(index)
10250            .expect("recognized token index must exist in the token store");
10251        let node = if error {
10252            ArenaRecognizedNode::ErrorToken { token }
10253        } else {
10254            ArenaRecognizedNode::Token { token }
10255        };
10256        self.recognition_arena.push_node(node)
10257    }
10258
10259    fn arena_missing_token_node(
10260        &mut self,
10261        token_type: i32,
10262        at_index: usize,
10263        text: String,
10264    ) -> RecognizedNodeId {
10265        let extra = self
10266            .recognition_arena
10267            .push_extra(RecognitionExtra::MissingToken {
10268                token_type,
10269                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
10270                text,
10271            });
10272        self.recognition_arena
10273            .push_node(ArenaRecognizedNode::MissingToken { extra })
10274    }
10275
10276    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
10277        let ArenaRuleSpec {
10278            rule_index,
10279            invoking_state,
10280            alt_number,
10281            start_index,
10282            stop_index,
10283            return_values,
10284            children,
10285        } = spec;
10286        let return_values = (!return_values.is_empty()).then(|| {
10287            self.recognition_arena
10288                .push_extra(RecognitionExtra::ReturnValues(return_values))
10289        });
10290        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
10291            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10292            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
10293            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10294            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
10295            stop_index: stop_index
10296                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
10297            return_values,
10298            children,
10299        })
10300    }
10301
10302    fn arena_boundary_node(&mut self, rule_index: usize) -> RecognizedNodeId {
10303        self.recognition_arena
10304            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
10305                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10306            })
10307    }
10308
10309    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
10310        *sequence = self.recognition_arena.prepend(*sequence, node);
10311    }
10312
10313    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
10314        self.last_recognition_arena_root = root;
10315        self.last_recognition_arena_diagnostics = diagnostics;
10316        #[cfg(feature = "perf-counters")]
10317        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
10318            let stats = self.recognition_arena_stats();
10319            #[allow(clippy::print_stderr)]
10320            {
10321                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
10322                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
10323                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
10324                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
10325                eprintln!("perf recognition_links_total={}", stats.total_links);
10326                eprintln!("perf recognition_links_live={}", stats.live_links);
10327                eprintln!("perf recognition_links_dead={}", stats.dead_links);
10328                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
10329                eprintln!("perf recognition_extras_total={}", stats.total_extras);
10330                eprintln!("perf recognition_extras_live={}", stats.live_extras);
10331                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
10332                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
10333            }
10334        }
10335    }
10336
10337    fn reset_recognition_arena(&mut self) {
10338        self.recognition_arena.reset();
10339        self.last_recognition_arena_root = NodeSeqId::EMPTY;
10340        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
10341    }
10342
10343    /// Normalizes the current token-stream cursor to the next parser-visible
10344    /// token before capturing a rule start boundary.
10345    fn current_visible_index(&mut self) -> usize {
10346        let index = self.input.index();
10347        self.input.seek(index);
10348        self.input.index()
10349    }
10350
10351    /// Reports whether a child rule reached EOF cleanly while also recording
10352    /// an EOF expectation from a longer path inside that child.
10353    fn child_expected_reaches_clean_eof(
10354        &mut self,
10355        children: &[RecognizeOutcome],
10356        expected: &ExpectedTokens,
10357    ) -> bool {
10358        let Some(index) = expected.index else {
10359            return false;
10360        };
10361        self.token_type_at(index) == TOKEN_EOF
10362            && children
10363                .iter()
10364                .any(|child| child.diagnostics.is_empty() && child.index == index)
10365    }
10366
10367    /// Finds the previous token visible to the parser before `index`.
10368    ///
10369    /// The token stream cursor skips hidden-channel tokens, so subtracting one
10370    /// from a visible-token index can point at whitespace. Parser intervals use
10371    /// this helper to stop at the previous visible token while preserving hidden
10372    /// text inside the rendered interval.
10373    fn previous_token_index(&self, index: usize) -> Option<usize> {
10374        self.input.previous_visible_token_index(index)
10375    }
10376
10377    /// Returns the token-stream index used as a rule stop boundary.
10378    ///
10379    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
10380    /// stop at `index` rather than at the previous visible token.
10381    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
10382        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
10383            Some(index)
10384        } else {
10385            self.previous_token_index(index)
10386        }
10387    }
10388
10389    /// Stop-token index for a rule's `@after` action, matching the boundary that
10390    /// `finish_rule` records on the rule context.
10391    ///
10392    /// A rule that matched EOF leaves the cursor parked on the EOF token
10393    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
10394    /// the current index rather than the previous visible token. Without this,
10395    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
10396    /// report the token before EOF (or `None` for empty input), diverging from
10397    /// the rule context that `finish_rule` builds.
10398    ///
10399    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
10400    /// rule ends right before EOF without matching it (the cursor is parked on
10401    /// EOF, but the rule did not consume it). Prefer
10402    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
10403    /// rule context already recorded with the real flag. Kept for callers without
10404    /// the rule tree in hand.
10405    #[must_use]
10406    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
10407        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
10408        self.rule_stop_token_index(current_index, consumed_eof)
10409    }
10410
10411    /// Stop-token index for a rule's `@after` action, taken from the stop token
10412    /// the rule context already recorded.
10413    ///
10414    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
10415    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
10416    /// the rule context — even when the rule ends immediately before EOF without
10417    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
10418    /// to the cursor-based inference only when the tree carries no rule stop.
10419    #[must_use]
10420    pub fn after_action_stop_index_for_tree(
10421        &mut self,
10422        tree: ParseTree,
10423        current_index: usize,
10424    ) -> Option<usize> {
10425        if let Some(stop) = self
10426            .node(tree)
10427            .as_rule()
10428            .and_then(crate::tree::RuleNodeView::stop_id)
10429        {
10430            return Some(stop.index());
10431        }
10432        self.after_action_stop_index(current_index)
10433    }
10434
10435    /// Start-token index for a rule's `@after` action, taken from the start token
10436    /// the rule context already recorded.
10437    ///
10438    /// `enter_rule` sets the rule context start to the first visible token (it
10439    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
10440    /// `$text` in an `@after` action aligned with the rule context — even when the
10441    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
10442    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
10443    /// the tree carries no rule start.
10444    #[must_use]
10445    pub fn after_action_start_index_for_tree(
10446        &self,
10447        tree: ParseTree,
10448        fallback_index: usize,
10449    ) -> usize {
10450        if let Some(start) = self
10451            .node(tree)
10452            .as_rule()
10453            .and_then(crate::tree::RuleNodeView::start_id)
10454        {
10455            return start.index();
10456        }
10457        fallback_index
10458    }
10459
10460    /// Returns the rule stop token for a selected parse path.
10461    ///
10462    /// EOF transitions do not advance the token-stream cursor, so an EOF match
10463    /// must use the current token rather than the previous visible token.
10464    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
10465        self.rule_stop_token_index(index, consumed_eof)
10466            .and_then(|token_index| self.token_id_at(token_index))
10467    }
10468
10469    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
10470    ///
10471    /// Generated Java reports the failed-predicate message at the current
10472    /// lookahead, then consumes until rule recovery can resume. The metadata
10473    /// runtime models the same visible tree shape by keeping skipped tokens as
10474    /// error nodes and returning from the active rule at EOF.
10475    fn predicate_failure_recovery(
10476        &mut self,
10477        request: PredicateFailureRecovery<'_>,
10478    ) -> RecognizeOutcome {
10479        let PredicateFailureRecovery {
10480            rule_index,
10481            index,
10482            message,
10483            member_values,
10484            return_values,
10485            rule_alt_number,
10486        } = request;
10487        let rule_name = self
10488            .rule_names()
10489            .get(rule_index)
10490            .map_or_else(|| rule_index.to_string(), Clone::clone);
10491        let diagnostic = diagnostic_for_token(
10492            self.token_at(index).as_ref(),
10493            format!("rule {rule_name} {message}"),
10494        );
10495        let mut reversed_nodes = NodeSeqId::EMPTY;
10496        let mut next_index = index;
10497        loop {
10498            let symbol = self.token_type_at(next_index);
10499            if symbol == TOKEN_EOF {
10500                break;
10501            }
10502            let error = self.arena_token_node(next_index, true);
10503            self.arena_prepend(&mut reversed_nodes, error);
10504            let after = self.consume_index(next_index, symbol);
10505            if after == next_index {
10506                break;
10507            }
10508            next_index = after;
10509        }
10510        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
10511        let diagnostics = self
10512            .recognition_arena
10513            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10514        RecognizeOutcome {
10515            index: next_index,
10516            consumed_eof: false,
10517            alt_number: rule_alt_number,
10518            member_values,
10519            return_values,
10520            diagnostics,
10521            decisions: Vec::new(),
10522            actions: Vec::new(),
10523            nodes,
10524        }
10525    }
10526
10527    /// Evaluates a user hook for a predicate coordinate that has no generated
10528    /// runtime table entry.
10529    fn parser_semantic_hook_result(
10530        &mut self,
10531        request: ParserSemanticHookRequest<'_>,
10532    ) -> Option<bool> {
10533        let ParserSemanticHookRequest {
10534            index,
10535            rule_index,
10536            pred_index,
10537            context,
10538            local_int_arg,
10539            member_values,
10540        } = request;
10541        let rule_name = self.rule_names().get(rule_index).cloned();
10542        self.input.seek(index);
10543        let input = &mut self.input;
10544        let semantic_hooks = &mut self.semantic_hooks;
10545        let mut ctx = ParserSemCtx {
10546            input,
10547            tree_storage: &self.tree,
10548            rule_index,
10549            coordinate_index: pred_index,
10550            rule_name,
10551            context,
10552            tree: None,
10553            local_int_arg,
10554            member_values,
10555            action: None,
10556        };
10557        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
10558    }
10559
10560    /// Re-inserts unknown-predicate coordinates recorded before a nested
10561    /// interpreted recognition, preserving order and skipping any the nested
10562    /// call already recorded, so a generated parent's fail-loud coordinates
10563    /// survive descending into an interpreted child.
10564    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
10565        if prior.is_empty() {
10566            return;
10567        }
10568        let mut merged = prior;
10569        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
10570            if !merged.contains(&coordinate) {
10571                merged.push(coordinate);
10572            }
10573        }
10574        self.unknown_predicate_hits = merged;
10575    }
10576
10577    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
10578    /// that has no entry in the generated predicate table.
10579    ///
10580    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
10581    /// the guarded path is abandoned; the parse entry surfaces the recorded
10582    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
10583    /// because a parse that consulted an unknown predicate is unreliable no
10584    /// matter which paths were ultimately selected.
10585    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
10586        apply_unknown_predicate_policy(
10587            self.unknown_predicate_policy,
10588            rule_index,
10589            pred_index,
10590            &mut self.unknown_predicate_hits,
10591        )
10592    }
10593
10594    /// Builds the fail-loud error for unknown predicate coordinates recorded
10595    /// by the current parse, if any.
10596    fn unknown_semantic_error(&self) -> Option<AntlrError> {
10597        use std::fmt::Write as _;
10598        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
10599            return None;
10600        }
10601        let mut message = String::new();
10602        for (rule_index, pred_index) in &self.unknown_predicate_hits {
10603            if !message.is_empty() {
10604                message.push_str("; ");
10605            }
10606            let _ = match self.rule_names().get(*rule_index) {
10607                Some(rule_name) => write!(
10608                    message,
10609                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
10610                ),
10611                None => write!(
10612                    message,
10613                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
10614                ),
10615            };
10616        }
10617        for (rule_index, source_state) in &self.unhandled_action_hits {
10618            if !message.is_empty() {
10619                message.push_str("; ");
10620            }
10621            let _ = match self.rule_names().get(*rule_index) {
10622                Some(rule_name) => write!(
10623                    message,
10624                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
10625                ),
10626                None => write!(
10627                    message,
10628                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
10629                ),
10630            };
10631        }
10632        Some(AntlrError::Unsupported(message))
10633    }
10634
10635    /// Evaluates one lowered predicate expression at the requested input
10636    /// position.
10637    ///
10638    /// This sits in the prediction hot loop, so the context borrows the
10639    /// speculative member state read-only and the rule name by reference —
10640    /// no per-evaluation allocation. Only the hook escape path materializes
10641    /// owned copies, and only when a hook is actually consulted.
10642    fn parser_semir_predicate_matches(
10643        &mut self,
10644        semantics: &ParserSemantics,
10645        predicate: &ParserSemanticPredicate,
10646        request: ParserSemanticHookRequest<'_>,
10647    ) -> bool {
10648        self.input.seek(request.index);
10649        let rule_name = self
10650            .data
10651            .rule_names()
10652            .get(request.rule_index)
10653            .map(String::as_str);
10654        let unknown_predicate_policy = self.unknown_predicate_policy;
10655        let mut ctx = ParserSemIrCtx {
10656            input: &mut self.input,
10657            tree_storage: &self.tree,
10658            semantic_hooks: &mut self.semantic_hooks,
10659            rule_index: request.rule_index,
10660            coordinate_index: request.pred_index,
10661            rule_name,
10662            context: request.context,
10663            local_int_arg: request.local_int_arg,
10664            member_values: request.member_values,
10665            invoked_predicates: &mut self.invoked_predicates,
10666            unknown_predicate_policy,
10667            unknown_predicate_hits: &mut self.unknown_predicate_hits,
10668        };
10669        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
10670    }
10671
10672    fn fast_parser_predicate_matches(
10673        &mut self,
10674        context: Option<FastPredicateContext<'_>>,
10675        transition: ParserTransition<'_>,
10676        index: usize,
10677    ) -> bool {
10678        let Some(context) = context else {
10679            return true;
10680        };
10681        let rule_index = transition.arg0() as usize;
10682        let pred_index = transition.arg1() as usize;
10683        let key = (index, rule_index, pred_index);
10684        if let Some(result) = self.fast_predicate_cache.get(&key) {
10685            return *result;
10686        }
10687        let result = self.parser_predicate_matches(PredicateEval {
10688            index,
10689            rule_index,
10690            pred_index,
10691            predicates: context.predicates,
10692            semantics: context.semantics,
10693            context: None,
10694            local_int_arg: None,
10695            member_values: context.member_values,
10696        });
10697        self.fast_predicate_cache.insert(key, result);
10698        result
10699    }
10700
10701    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
10702        let PredicateEval {
10703            index,
10704            rule_index,
10705            pred_index,
10706            predicates,
10707            semantics,
10708            context,
10709            local_int_arg,
10710            member_values,
10711        } = eval;
10712        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
10713            semantics
10714                .predicates
10715                .iter()
10716                .find(|predicate| {
10717                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
10718                })
10719                .map(|predicate| (semantics, predicate))
10720        }) {
10721            return self.parser_semir_predicate_matches(
10722                semantics,
10723                predicate,
10724                ParserSemanticHookRequest {
10725                    index,
10726                    rule_index,
10727                    pred_index,
10728                    context,
10729                    local_int_arg,
10730                    member_values,
10731                },
10732            );
10733        }
10734        let Some((_, _, predicate)) = predicates
10735            .iter()
10736            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
10737        else {
10738            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
10739                index,
10740                rule_index,
10741                pred_index,
10742                context,
10743                local_int_arg,
10744                member_values,
10745            }) {
10746                return result;
10747            }
10748            return self.unknown_predicate_result(rule_index, pred_index);
10749        };
10750        self.input.seek(index);
10751        match predicate {
10752            ParserPredicate::True => true,
10753            ParserPredicate::False => false,
10754            ParserPredicate::FalseWithMessage { .. } => false,
10755            ParserPredicate::Invoke { value } => {
10756                let key = (rule_index, pred_index);
10757                if !self.invoked_predicates.contains(&key) {
10758                    self.invoked_predicates.push(key);
10759                    use std::io::Write as _;
10760                    let mut stdout = std::io::stdout().lock();
10761                    let _ = writeln!(stdout, "eval={value}");
10762                }
10763                *value
10764            }
10765            ParserPredicate::LookaheadTextEquals { offset, text } => self
10766                .input
10767                .lt(*offset)
10768                .is_some_and(|token| Token::text(&token) == Some(*text)),
10769            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
10770                self.la(*offset) != *token_type
10771            }
10772            ParserPredicate::TokenPairAdjacent => {
10773                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
10774                    return false;
10775                };
10776                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
10777                    return false;
10778                };
10779                first + 1 == second
10780            }
10781            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
10782                .and_then(|context| {
10783                    context
10784                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
10785                        .next()
10786                        .map(crate::tree::RuleNodeView::text)
10787                })
10788                .is_none_or(|actual| actual != *text),
10789            ParserPredicate::LocalIntEquals { value } => {
10790                local_int_arg.is_none_or(|(_, actual)| actual == *value)
10791            }
10792            ParserPredicate::LocalIntLessOrEqual { value } => {
10793                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
10794            }
10795            ParserPredicate::MemberModuloEquals {
10796                member,
10797                modulus,
10798                value,
10799                equals,
10800            } => {
10801                if *modulus == 0 {
10802                    return false;
10803                }
10804                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
10805                (actual == *value) == *equals
10806            }
10807            ParserPredicate::MemberEquals {
10808                member,
10809                value,
10810                equals,
10811            } => {
10812                let actual = member_values.get(member).copied().unwrap_or_default();
10813                (actual == *value) == *equals
10814            }
10815        }
10816    }
10817
10818    /// Returns a generated fail-option message for a predicate coordinate.
10819    fn parser_predicate_failure_message(
10820        &self,
10821        rule_index: usize,
10822        pred_index: usize,
10823        predicates: &[(usize, usize, ParserPredicate)],
10824    ) -> Option<&'static str> {
10825        predicates
10826            .iter()
10827            .find_map(|(rule, pred, predicate)| match predicate {
10828                ParserPredicate::FalseWithMessage { message }
10829                    if *rule == rule_index && *pred == pred_index =>
10830                {
10831                    Some(*message)
10832                }
10833                _ => None,
10834            })
10835    }
10836
10837    /// Returns a generated fail-option message for a `SemIR` predicate
10838    /// coordinate.
10839    pub fn parser_semantic_ir_predicate_failure_message(
10840        &self,
10841        rule_index: usize,
10842        pred_index: usize,
10843        semantics: &ParserSemantics,
10844    ) -> Option<&'static str> {
10845        semantics
10846            .predicates
10847            .iter()
10848            .find(|predicate| {
10849                predicate.rule_index == rule_index && predicate.pred_index == pred_index
10850            })
10851            .and_then(|predicate| predicate.failure_message)
10852    }
10853
10854    /// Returns the token-stream index after consuming `symbol` at `index`.
10855    ///
10856    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
10857    /// index stable and rely on `consumed_eof` to record that EOF was matched.
10858    /// The parser's stream cursor is left untouched: speculative recognition
10859    /// reads ahead by absolute index, so paying for `seek` on every visited
10860    /// state would dominate the hot path. Real consumption is committed by
10861    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
10862    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
10863        if symbol == TOKEN_EOF {
10864            return index;
10865        }
10866        self.input.next_visible_after(index)
10867    }
10868
10869    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
10870    /// decision that failed after consuming a shared prefix.
10871    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
10872        let text = display_input_text(&self.input.text(start_index, error_index));
10873        diagnostic_for_token(
10874            self.token_at(error_index).as_ref(),
10875            format!("no viable alternative at input '{text}'"),
10876        )
10877    }
10878
10879    /// Selects the diagnostic for a failed consuming transition after all
10880    /// recovery repairs have been ruled out.
10881    fn recovery_failure_diagnostic(
10882        &self,
10883        index: usize,
10884        decision_start_index: Option<usize>,
10885        expected_symbols: &BTreeSet<i32>,
10886    ) -> ParserDiagnostic {
10887        if expected_symbols.len() > 1 {
10888            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
10889                return self.no_viable_alternative(decision_start, index);
10890            }
10891        }
10892        diagnostic_for_token(
10893            self.token_at(index).as_ref(),
10894            format!(
10895                "mismatched input {} expecting {}",
10896                self.token_at(index)
10897                    .as_ref()
10898                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10899                self.expected_symbols_display(expected_symbols)
10900            ),
10901        )
10902    }
10903
10904    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
10905    /// instead of inserting missing tokens in the caller.
10906    fn eof_rule_recovery_diagnostic(
10907        &self,
10908        index: usize,
10909        expected_symbols: &BTreeSet<i32>,
10910        expected: &ExpectedTokens,
10911    ) -> ParserDiagnostic {
10912        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
10913            &expected.symbols
10914        } else {
10915            expected_symbols
10916        };
10917        diagnostic_for_token(
10918            self.token_at(index).as_ref(),
10919            format!(
10920                "mismatched input {} expecting {}",
10921                self.token_at(index)
10922                    .as_ref()
10923                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10924                self.expected_symbols_display(symbols)
10925            ),
10926        )
10927    }
10928
10929    /// Returns token text for a buffered token interval used by generated
10930    /// `$text` actions.
10931    ///
10932    /// ANTLR treats EOF as a range boundary rather than printable input text,
10933    /// even when an action interval explicitly stops at the EOF token.
10934    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
10935        let Some(stop) = stop else {
10936            return String::new();
10937        };
10938        let stop = if self
10939            .token_at(stop)
10940            .is_some_and(|token| token.token_type() == TOKEN_EOF)
10941        {
10942            let Some(previous) = self.previous_token_index(stop) else {
10943                return String::new();
10944            };
10945            previous
10946        } else {
10947            stop
10948        };
10949        self.input.text(start, stop)
10950    }
10951
10952    /// Resets per-parse prediction diagnostics while keeping the parser-level
10953    /// reporting flag configured by generated harness code.
10954    fn clear_prediction_diagnostics(&mut self) {
10955        self.prediction_diagnostics.clear();
10956        self.reported_prediction_diagnostics.clear();
10957    }
10958
10959    /// Drops every per-parse cache that depends on ATN identity or pins
10960    /// recovery-symbol allocations.
10961    ///
10962    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
10963    /// same parser instance can legally be driven against different grammars
10964    /// in sequence. The four caches reset here are keyed by raw ATN
10965    /// coordinates (state numbers, rule indexes) and would silently hand back
10966    /// entries from a previous ATN if reused — pruning lookahead against the
10967    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
10968    /// for the rest of the process. Clearing them on every parse entry keeps
10969    /// the perf wins (caches still amortize within one parse) without making
10970    /// long-lived parsers leak memory or surface stale ATN data:
10971    ///
10972    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
10973    ///   functions of the ATN's state graph.
10974    /// * `state_expected_cache`, `state_expected_token_cache`,
10975    ///   `rule_stop_reach_cache`, and
10976    ///   `recovery_symbols_intern` together form
10977    ///   the identity invariant that lets `FastRecognizeKey` hash
10978    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
10979    ///   so a stale interned `Rc` cannot outlive its map entry.
10980    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
10981    ///   it is retained here and invalidated lazily when the ATN changes.
10982    fn reset_per_parse_caches(&mut self) {
10983        self.rule_first_set_cache.clear();
10984        self.decision_lookahead_cache.clear();
10985        self.ll1_decision_cache.clear();
10986        self.fast_predicate_cache.clear();
10987        self.rule_stop_reach_cache.clear();
10988        self.clean_memo_mode = CleanMemoMode::Probe;
10989        self.clean_memo_probe_seen.clear();
10990        self.clean_memo_probe_samples = 0;
10991        self.clean_memo_probe_repeats = 0;
10992        self.clean_memo_sparse_samples = 0;
10993        self.recovery_symbols_intern.clear();
10994        self.state_expected_cache.clear();
10995        self.state_expected_token_cache.clear();
10996    }
10997
10998    /// Buffers ANTLR-style diagnostic-listener messages for decision states
10999    /// where multiple clean alternatives survive full-context recognition.
11000    fn record_prediction_diagnostics(
11001        &mut self,
11002        atn: &Atn,
11003        state: AtnState<'_>,
11004        start_index: usize,
11005        outcomes: &[RecognizeOutcome],
11006    ) {
11007        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
11008            return;
11009        }
11010        let Some(decision) = atn
11011            .decision_to_state()
11012            .iter()
11013            .position(|state_number| state_number == state.state_number())
11014        else {
11015            return;
11016        };
11017        let Some(rule_index) = state.rule_index() else {
11018            return;
11019        };
11020        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
11021        for outcome in outcomes
11022            .iter()
11023            .filter(|outcome| outcome.diagnostics.is_empty())
11024        {
11025            let Some(alt) = outcome.decisions.first() else {
11026                continue;
11027            };
11028            alts_by_end
11029                .entry(outcome.index)
11030                .or_default()
11031                .insert(alt + 1);
11032        }
11033        let Some((&end_index, ambig_alts)) = alts_by_end
11034            .iter()
11035            .filter(|(_, alts)| alts.len() > 1)
11036            .max_by_key(|(end, _)| *end)
11037        else {
11038            return;
11039        };
11040        let rule_name = self
11041            .rule_names()
11042            .get(rule_index)
11043            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
11044        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
11045        let input = display_input_text(&self.input.text(start_index, stop_index));
11046        let alts = ambig_alts
11047            .iter()
11048            .map(usize::to_string)
11049            .collect::<Vec<_>>()
11050            .join(", ");
11051        let key = (decision, start_index, format!("{alts}:{input}"));
11052        if !self.reported_prediction_diagnostics.insert(key) {
11053            return;
11054        }
11055        let start_diagnostic = diagnostic_for_token(
11056            self.token_at(start_index),
11057            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
11058        );
11059        let stop_diagnostic = diagnostic_for_token(
11060            self.token_at(stop_index),
11061            format!(
11062                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
11063            ),
11064        );
11065        self.prediction_diagnostics.push(start_diagnostic);
11066        self.prediction_diagnostics.push(stop_diagnostic);
11067    }
11068
11069    /// Formats the tokens expected from an ATN state using ANTLR display names.
11070    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11071        expected_symbols_display(
11072            &state_expected_symbols(atn, state_number),
11073            self.vocabulary(),
11074        )
11075    }
11076
11077    /// Expected-token set at the parser's current ATN state — ANTLR's
11078    /// `getExpectedTokens()`. Generated recognizers expose this as
11079    /// `self.expected_tokens()` for embedded test actions
11080    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
11081    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
11082        let state = usize::try_from(self.data().state()).unwrap_or(0);
11083        ExpectedTokenSet {
11084            symbols: state_expected_symbols(atn, state),
11085        }
11086    }
11087
11088    /// Enables the bail error strategy: the first syntax error aborts the
11089    /// parse instead of recovering.
11090    pub const fn set_bail_on_error(&mut self, bail: bool) {
11091        self.bail_on_error = bail;
11092    }
11093
11094    /// Whether the bail error strategy is active.
11095    #[must_use]
11096    pub const fn bail_on_error(&self) -> bool {
11097        self.bail_on_error
11098    }
11099
11100    /// Names of the rules on the live invocation stack, current rule first —
11101    /// ANTLR's `getRuleInvocationStack()`.
11102    pub fn rule_invocation_stack(&self) -> Vec<String> {
11103        self.rule_context_stack
11104            .iter()
11105            .rev()
11106            .map(|frame| {
11107                self.data()
11108                    .rule_names()
11109                    .get(frame.rule_index)
11110                    .cloned()
11111                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
11112            })
11113            .collect()
11114    }
11115
11116    /// Invoking-state chain for the active rule context, current rule first.
11117    ///
11118    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
11119    pub fn active_invocation_states(&self) -> Vec<isize> {
11120        self.rule_context_stack
11121            .iter()
11122            .skip(1)
11123            .rev()
11124            .map(|frame| frame.invoking_state)
11125            .collect()
11126    }
11127
11128    /// Formats a buffered token in ANTLR's diagnostic token display form.
11129    pub fn token_display_at(&self, index: usize) -> Option<String> {
11130        self.token_at(index).map(|token| format!("{token}"))
11131    }
11132}
11133
11134impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
11135where
11136    S: TokenSource,
11137    H: SemanticHooks,
11138{
11139    fn parse_rule(
11140        &mut self,
11141        rule_index: usize,
11142        invoking_state: isize,
11143        precedence: i32,
11144    ) -> DirectAdaptiveParseResult<ParseTree> {
11145        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
11146            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
11147        )?;
11148        let stop_state = self
11149            .atn
11150            .rule_to_stop_state()
11151            .get(rule_index)
11152            .filter(|state| *state != usize::MAX)
11153            .ok_or(DirectAdaptiveParseControl::Fallback(
11154                DirectAdaptiveFallback::MissingAtn,
11155            ))?;
11156        let start_index = self.parser.current_visible_index();
11157        let mut context = ParserRuleContext::new(rule_index, invoking_state);
11158        if let Some(token) = self.parser.token_id_at(start_index) {
11159            self.parser.set_context_start(&mut context, token);
11160        }
11161        let mut state_number = start_state;
11162        let mut consumed_eof = false;
11163        while state_number != stop_state {
11164            self.step()?;
11165            let (transition, boundary) = self.next_transition(state_number, precedence)?;
11166            if boundary.is_some() {
11167                return Err(DirectAdaptiveParseControl::Fallback(
11168                    DirectAdaptiveFallback::LeftRecursiveBoundary,
11169                ));
11170            }
11171            match transition.data() {
11172                Transition::Epsilon { target } => {
11173                    state_number = target;
11174                }
11175                Transition::Precedence {
11176                    target,
11177                    precedence: transition_precedence,
11178                } => {
11179                    if transition_precedence < precedence {
11180                        return Err(DirectAdaptiveParseControl::Fallback(
11181                            DirectAdaptiveFallback::Precedence,
11182                        ));
11183                    }
11184                    state_number = target;
11185                }
11186                Transition::Rule {
11187                    rule_index,
11188                    follow_state,
11189                    precedence: rule_precedence,
11190                    ..
11191                } => {
11192                    let child = self.parse_rule(
11193                        rule_index,
11194                        invoking_state_number(state_number),
11195                        rule_precedence,
11196                    )?;
11197                    if self.parser.build_parse_trees {
11198                        self.parser.tree.add_child(&mut context, child);
11199                    }
11200                    state_number = follow_state;
11201                }
11202                Transition::Atom { .. }
11203                | Transition::Range { .. }
11204                | Transition::Set { .. }
11205                | Transition::NotSet { .. }
11206                | Transition::Wildcard { .. } => {
11207                    let (matched_eof, child) = self.consume_transition(transition)?;
11208                    consumed_eof |= matched_eof;
11209                    if let Some(child) = child {
11210                        self.parser.tree.add_child(&mut context, child);
11211                    }
11212                    state_number = transition.target();
11213                }
11214                Transition::Predicate { .. } => {
11215                    return Err(DirectAdaptiveParseControl::Fallback(
11216                        DirectAdaptiveFallback::Predicate,
11217                    ));
11218                }
11219                Transition::Action { .. } => {
11220                    return Err(DirectAdaptiveParseControl::Fallback(
11221                        DirectAdaptiveFallback::Action,
11222                    ));
11223                }
11224            }
11225        }
11226
11227        let stop_index = self
11228            .parser
11229            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
11230        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
11231            self.parser.set_context_stop(&mut context, token);
11232        }
11233        Ok(self.parser.rule_node(context))
11234    }
11235
11236    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
11237        self.steps += 1;
11238        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
11239            return Err(DirectAdaptiveParseControl::Fallback(
11240                DirectAdaptiveFallback::StepLimit,
11241            ));
11242        }
11243        Ok(())
11244    }
11245
11246    fn next_transition(
11247        &mut self,
11248        state_number: usize,
11249        precedence: i32,
11250    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
11251        let state = self
11252            .atn
11253            .state(state_number)
11254            .ok_or(DirectAdaptiveParseControl::Fallback(
11255                DirectAdaptiveFallback::MissingAtn,
11256            ))?;
11257        if state.is_rule_stop() {
11258            return Err(DirectAdaptiveParseControl::Fallback(
11259                DirectAdaptiveFallback::RuleStop,
11260            ));
11261        }
11262        let transition_index =
11263            self.transition_index(state_number, state.transitions().len(), precedence)?;
11264        let transition = state.transitions().get(transition_index).ok_or(
11265            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
11266        )?;
11267        let boundary = match &transition.data() {
11268            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
11269                left_recursive_boundary(self.atn, state, *target)
11270            }
11271            _ => None,
11272        };
11273        Ok((transition, boundary))
11274    }
11275
11276    fn transition_index(
11277        &mut self,
11278        state_number: usize,
11279        transition_count: usize,
11280        precedence: i32,
11281    ) -> DirectAdaptiveParseResult<usize> {
11282        match transition_count {
11283            0 => Err(DirectAdaptiveParseControl::Fallback(
11284                DirectAdaptiveFallback::NoTransition,
11285            )),
11286            1 => Ok(0),
11287            _ => {
11288                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
11289                    return Ok(alt);
11290                }
11291                let decision = self
11292                    .decision_by_state
11293                    .get(state_number)
11294                    .and_then(|decision| *decision)
11295                    .ok_or(DirectAdaptiveParseControl::Fallback(
11296                        DirectAdaptiveFallback::UnknownDecision,
11297                    ))?;
11298                let prediction = self
11299                    .simulator
11300                    .adaptive_predict_stream_info_with_precedence(
11301                        decision,
11302                        direct_precedence(precedence),
11303                        &mut self.parser.input,
11304                    )
11305                    .map_err(|_| {
11306                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
11307                    })?;
11308                if prediction.has_semantic_context {
11309                    return Err(DirectAdaptiveParseControl::Fallback(
11310                        DirectAdaptiveFallback::SemanticContext,
11311                    ));
11312                }
11313                prediction
11314                    .alt
11315                    .checked_sub(1)
11316                    .filter(|index| *index < transition_count)
11317                    .ok_or(DirectAdaptiveParseControl::Fallback(
11318                        DirectAdaptiveFallback::InvalidAlt,
11319                    ))
11320            }
11321        }
11322    }
11323
11324    fn ll1_transition_index(
11325        &mut self,
11326        state_number: usize,
11327        transition_count: usize,
11328    ) -> DirectAdaptiveParseResult<Option<usize>> {
11329        let state = self
11330            .atn
11331            .state(state_number)
11332            .ok_or(DirectAdaptiveParseControl::Fallback(
11333                DirectAdaptiveFallback::MissingAtn,
11334            ))?;
11335        if state.precedence_rule_decision() {
11336            return Ok(None);
11337        }
11338        let Some(rule_stop) = state
11339            .rule_index()
11340            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
11341        else {
11342            return Ok(None);
11343        };
11344        let symbol = self.parser.input.la_token(1);
11345        let entry = self
11346            .parser
11347            .cached_decision_lookahead(self.atn, state, rule_stop);
11348        Ok(
11349            ll1_greedy_alt(&entry, symbol, state.non_greedy())
11350                .filter(|alt| *alt < transition_count),
11351        )
11352    }
11353
11354    fn consume_transition(
11355        &mut self,
11356        transition: ParserTransition<'_>,
11357    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
11358        let symbol = self.parser.input.la_token(1);
11359        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
11360            return Err(DirectAdaptiveParseControl::Fallback(
11361                DirectAdaptiveFallback::TokenMismatch,
11362            ));
11363        }
11364        let token = self
11365            .parser
11366            .input
11367            .lt_id(1)
11368            .ok_or(DirectAdaptiveParseControl::Fallback(
11369                DirectAdaptiveFallback::TokenMismatch,
11370            ))?;
11371        let matched_eof = symbol == TOKEN_EOF;
11372        if !matched_eof {
11373            self.parser.consume();
11374        }
11375        let child = self
11376            .parser
11377            .build_parse_trees
11378            .then(|| self.parser.terminal_tree(token));
11379        Ok((matched_eof, child))
11380    }
11381}
11382
11383/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
11384/// transformed left-recursive rule.
11385fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
11386    if !state.precedence_rule_decision() {
11387        return None;
11388    }
11389    let target_state = atn.state(target)?;
11390    if target_state.kind() == AtnStateKind::LoopEnd {
11391        return None;
11392    }
11393    state.rule_index()
11394}
11395
11396/// Selects the first outer alternative observed for a rule path.
11397///
11398/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
11399/// outer decision. The metadata recognizer only needs this when a generated
11400/// grammar opts into that target template; otherwise the value remains `0` and
11401/// parse-tree rendering is unchanged.
11402fn next_alt_number(
11403    state: AtnState<'_>,
11404    transition_count: usize,
11405    transition_index: usize,
11406    current_alt_number: usize,
11407    track_alt_numbers: bool,
11408) -> usize {
11409    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
11410        return current_alt_number;
11411    }
11412    if matches!(
11413        state.kind(),
11414        AtnStateKind::Basic
11415            | AtnStateKind::BlockStart
11416            | AtnStateKind::PlusBlockStart
11417            | AtnStateKind::StarBlockStart
11418            | AtnStateKind::StarLoopEntry
11419    ) && !state.precedence_rule_decision()
11420    {
11421        return transition_index + 1;
11422    }
11423    current_alt_number
11424}
11425
11426/// Converts an ATN state number into the signed invoking-state slot used by
11427/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
11428fn invoking_state_number(state_number: usize) -> isize {
11429    isize::try_from(state_number).unwrap_or(isize::MAX)
11430}
11431
11432const fn packed_i32(value: u32) -> i32 {
11433    i32::from_le_bytes(value.to_le_bytes())
11434}
11435
11436fn direct_precedence(precedence: i32) -> usize {
11437    usize::try_from(precedence.max(0)).unwrap_or_default()
11438}
11439
11440fn token_input_display(token: &impl Token) -> String {
11441    format!("'{}'", token.text().unwrap_or("<EOF>"))
11442}
11443
11444fn display_input_text(text: &str) -> String {
11445    let mut out = String::new();
11446    for ch in text.chars() {
11447        match ch {
11448            '\n' => out.push_str("\\n"),
11449            '\r' => out.push_str("\\r"),
11450            '\t' => out.push_str("\\t"),
11451            other => out.push(other),
11452        }
11453    }
11454    out
11455}
11456
11457fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
11458    let (line, column) = token.map_or((0, 0), |token| (token.line(), token.column()));
11459    ParserDiagnostic {
11460        line,
11461        column,
11462        message,
11463    }
11464}
11465
11466fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
11467    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
11468}
11469
11470fn expected_symbols_display_iter(
11471    symbols: impl IntoIterator<Item = i32>,
11472    vocabulary: &Vocabulary,
11473) -> String {
11474    let items = symbols
11475        .into_iter()
11476        .map(|symbol| expected_symbol_display(symbol, vocabulary))
11477        .collect::<Vec<_>>();
11478    if let [single] = items.as_slice() {
11479        return single.clone();
11480    }
11481    format!("{{{}}}", items.join(", "))
11482}
11483
11484fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
11485    if symbol == TOKEN_EOF {
11486        return "<EOF>".to_owned();
11487    }
11488    vocabulary.display_name(symbol)
11489}
11490
11491fn caller_follow_token_info_for_stream<S: TokenSource>(
11492    input: &mut CommonTokenStream<S>,
11493    index: usize,
11494) -> (i32, bool, bool) {
11495    // Generated callers own statement separators; leave them available when
11496    // an interpreted child rule can either stop before or consume one.
11497    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
11498        input.fill();
11499    }
11500    let token_type = input.token_type_at_index(index);
11501    let visible_channel = input.channel();
11502    let token = input.get(index);
11503    let is_boundary = token
11504        .as_ref()
11505        .and_then(Token::text)
11506        .is_some_and(is_caller_follow_boundary_text);
11507    let is_boundary_gap = token.as_ref().is_some_and(|token| {
11508        token.channel() != visible_channel || is_caller_follow_boundary_gap_text(token.text())
11509    });
11510    (token_type, is_boundary, is_boundary_gap)
11511}
11512
11513fn is_caller_follow_boundary_text(text: &str) -> bool {
11514    text.chars().any(|ch| ch == ';' || ch == '\n')
11515        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11516}
11517
11518fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
11519    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11520}
11521
11522/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
11523/// Inline insertion in those precedence loops can synthesize a missing operand
11524/// before an operator and then block the legitimate loop-exit path.
11525fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
11526    let Some(rule_index) = state.rule_index() else {
11527        return false;
11528    };
11529    atn.rule_to_start_state()
11530        .get(rule_index)
11531        .and_then(|state_number| atn.state(state_number))
11532        .is_some_and(AtnState::left_recursive_rule)
11533}
11534
11535/// Picks the better of two `parse_atn_rule` passes (with and without the
11536/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
11537/// recovered one; among recovered outcomes the second pass is preferred
11538/// because the no-prefilter walk reaches ANTLR-style recovery inside child
11539/// rules. If both passes failed, the second pass's expected-token snapshot
11540/// is returned so the caller renders the same diagnostic ANTLR would.
11541fn select_better_top_outcome(
11542    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11543    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11544    arena: &RecognitionArena,
11545) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
11546    match (first, second) {
11547        (Ok(first), Ok(second)) => {
11548            if arena.diagnostics(first.0.diagnostics).next().is_none() {
11549                Ok(first)
11550            } else {
11551                Ok(second)
11552            }
11553        }
11554        (Ok(first), Err(_)) => Ok(first),
11555        (Err(_), Ok(second)) => Ok(second),
11556        (Err(_), Err(second_expected)) => Err(second_expected),
11557    }
11558}
11559
11560/// Chooses the outermost parse result that consumed the most input.
11561///
11562/// The recognizer intentionally keeps shorter endpoints available while walking
11563/// nested rule transitions so callers can satisfy following tokens such as
11564/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
11565fn select_best_fast_outcome(
11566    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
11567    prediction_mode: PredictionMode,
11568    caller_follow: Option<&TokenBitSet>,
11569    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
11570    arena: &RecognitionArena,
11571) -> Option<FastRecognizeOutcome> {
11572    let mut best = None;
11573    let mut best_caller_follow = None;
11574    for outcome in outcomes {
11575        if matches!(
11576            prediction_mode,
11577            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11578        ) && outcome.diagnostics.is_empty()
11579            && let Some(follow) = caller_follow
11580        {
11581            let (token_type, is_boundary, _) = token_info_at(outcome.index);
11582            if is_boundary && follow.contains(token_type) {
11583                let replace =
11584                    best_caller_follow
11585                        .as_ref()
11586                        .is_none_or(|existing: &FastRecognizeOutcome| {
11587                            (outcome.index, outcome.consumed_eof)
11588                                < (existing.index, existing.consumed_eof)
11589                        });
11590                if replace {
11591                    best_caller_follow = Some(outcome);
11592                }
11593            }
11594        }
11595        let Some(existing) = best else {
11596            best = Some(outcome);
11597            continue;
11598        };
11599        let outcome_position = (outcome.index, outcome.consumed_eof);
11600        let best_position = (existing.index, existing.consumed_eof);
11601        let better = match prediction_mode {
11602            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
11603                outcome_position,
11604                outcome.diagnostics,
11605                best_position,
11606                existing.diagnostics,
11607                arena,
11608            ),
11609            PredictionMode::Sll => outcome.index > existing.index,
11610        };
11611        best = Some(if better { outcome } else { existing });
11612    }
11613    let should_use_caller_follow =
11614        best_caller_follow
11615            .as_ref()
11616            .zip(best.as_ref())
11617            .is_some_and(|(candidate, selected)| {
11618                if !selected.diagnostics.is_empty() {
11619                    return true;
11620                }
11621                candidate.index < selected.index
11622                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
11623            });
11624    if should_use_caller_follow {
11625        best_caller_follow
11626    } else {
11627        best
11628    }
11629}
11630
11631fn select_best_outcome(
11632    outcomes: impl Iterator<Item = RecognizeOutcome>,
11633    prediction_mode: PredictionMode,
11634    arena: &RecognitionArena,
11635) -> Option<RecognizeOutcome> {
11636    let outcomes = outcomes.collect::<Vec<_>>();
11637    let prefer_first_tie = outcomes
11638        .iter()
11639        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
11640    outcomes.into_iter().reduce(|best, outcome| {
11641        let outcome_position = (outcome.index, outcome.consumed_eof);
11642        let best_position = (best.index, best.consumed_eof);
11643        let better = match prediction_mode {
11644            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
11645                outcome_is_better(
11646                    outcome_position,
11647                    outcome.diagnostics,
11648                    best_position,
11649                    best.diagnostics,
11650                    arena,
11651                ) || (!prefer_first_tie
11652                    && outcome_position == best_position
11653                    && arena.diagnostics_len(outcome.diagnostics)
11654                        == arena.diagnostics_len(best.diagnostics)
11655                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
11656                        == arena.diagnostics_recovery_rank(best.diagnostics)
11657                    && (outcome.decisions < best.decisions
11658                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
11659            }
11660            PredictionMode::Sll => {
11661                outcome_position > best_position
11662                    || (outcome_position == best_position
11663                        && !prefer_first_tie
11664                        && (outcome.decisions < best.decisions
11665                            || (outcome.decisions == best.decisions
11666                                && outcome_is_better(
11667                                    outcome_position,
11668                                    outcome.diagnostics,
11669                                    best_position,
11670                                    best.diagnostics,
11671                                    arena,
11672                                ))))
11673            }
11674        };
11675        if better {
11676            return outcome;
11677        }
11678        best
11679    })
11680}
11681
11682/// Records the serialized transition order at parser decision states.
11683///
11684/// When two clean paths consume the same input, ANTLR's adaptive prediction
11685/// chooses by alternative order. Keeping this compact trace lets the metadata
11686/// recognizer distinguish greedy and non-greedy optional blocks without a full
11687/// prediction simulator.
11688fn transition_decision(
11689    atn: &Atn,
11690    state: AtnState<'_>,
11691    transition_count: usize,
11692    transition_index: usize,
11693    predicates: &[(usize, usize, ParserPredicate)],
11694) -> Option<usize> {
11695    if transition_count <= 1
11696        || state.precedence_rule_decision()
11697        || decision_reaches_unsupported_predicate(atn, state, predicates)
11698    {
11699        return None;
11700    }
11701    Some(transition_index)
11702}
11703
11704/// Reports whether a state should reset the active no-viable decision start.
11705///
11706/// Loop entry/back states are continuations of the surrounding adaptive
11707/// prediction; resetting at those states would turn LL-star failures back into
11708/// ordinary mismatches.
11709fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
11710    transition_count > 1
11711        && !matches!(
11712            state.kind(),
11713            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
11714        )
11715}
11716
11717/// Marks a farthest expected-token set as no-viable when multiple alternatives
11718/// failed after the active decision had already consumed input.
11719fn record_no_viable_if_ambiguous(
11720    expected: &mut ExpectedTokens,
11721    decision_start_index: Option<usize>,
11722    index: usize,
11723) {
11724    if expected.index == Some(index) && expected.symbols.len() > 1 {
11725        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11726            expected.record_no_viable(decision_start, index);
11727        }
11728    }
11729}
11730
11731/// Records a no-viable decision caused by a failed semantic predicate before
11732/// any consuming transition can contribute an expected-token set.
11733const fn record_predicate_no_viable(
11734    expected: &mut ExpectedTokens,
11735    decision_start_index: Option<usize>,
11736    index: usize,
11737) {
11738    if let Some(decision_start) = decision_start_index {
11739        expected.record_no_viable(decision_start, index);
11740    }
11741}
11742
11743/// Returns the active decision start only when the error is past that start.
11744const fn no_viable_decision_start(
11745    decision_start_index: Option<usize>,
11746    index: usize,
11747) -> Option<usize> {
11748    match decision_start_index {
11749        Some(start) if index > start => Some(start),
11750        _ => None,
11751    }
11752}
11753
11754/// Restores expected-token bookkeeping when a child rule found a clean
11755/// consuming path; failures in longer child alternatives should not pollute the
11756/// caller's final expectation set.
11757fn restore_expected(
11758    children: &[RecognizeOutcome],
11759    child_start_index: usize,
11760    expected: &mut ExpectedTokens,
11761    snapshot: ExpectedTokens,
11762    preserve_child_expected: bool,
11763) {
11764    if preserve_child_expected {
11765        return;
11766    }
11767    if children
11768        .iter()
11769        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
11770    {
11771        *expected = snapshot;
11772    }
11773}
11774
11775/// Reports whether a decision can reach a predicate the generator did not
11776/// translate. Static alternative order is unsafe for those context predicates.
11777fn decision_reaches_unsupported_predicate(
11778    atn: &Atn,
11779    state: AtnState<'_>,
11780    predicates: &[(usize, usize, ParserPredicate)],
11781) -> bool {
11782    state.transitions().iter().any(|transition| {
11783        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
11784    })
11785}
11786
11787/// Walks epsilon-like edges from one transition to find unsupported predicates.
11788fn transition_reaches_unsupported_predicate(
11789    atn: &Atn,
11790    transition: ParserTransition<'_>,
11791    predicates: &[(usize, usize, ParserPredicate)],
11792    visited: &mut BTreeSet<usize>,
11793) -> bool {
11794    match &transition.data() {
11795        Transition::Predicate {
11796            rule_index,
11797            pred_index,
11798            ..
11799        } => !predicates
11800            .iter()
11801            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
11802        Transition::Epsilon { target }
11803        | Transition::Action { target, .. }
11804        | Transition::Rule { target, .. } => {
11805            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
11806        }
11807        Transition::Precedence { .. }
11808        | Transition::Atom { .. }
11809        | Transition::Range { .. }
11810        | Transition::Set { .. }
11811        | Transition::NotSet { .. }
11812        | Transition::Wildcard { .. } => false,
11813    }
11814}
11815
11816/// Finds an unsupported predicate reachable before a consuming transition.
11817fn state_reaches_unsupported_predicate(
11818    atn: &Atn,
11819    state_number: usize,
11820    predicates: &[(usize, usize, ParserPredicate)],
11821    visited: &mut BTreeSet<usize>,
11822) -> bool {
11823    if !visited.insert(state_number) {
11824        return false;
11825    }
11826    let Some(state) = atn.state(state_number) else {
11827        return false;
11828    };
11829    state.transitions().iter().any(|transition| {
11830        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
11831    })
11832}
11833
11834/// Adds a decision step to the front of an already-recognized suffix path.
11835fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
11836    if let Some(decision) = decision {
11837        outcome.decisions.insert(0, decision);
11838    }
11839}
11840
11841fn outcome_is_better(
11842    outcome_position: (usize, bool),
11843    outcome_diagnostics: DiagnosticSeqId,
11844    best_position: (usize, bool),
11845    best_diagnostics: DiagnosticSeqId,
11846    arena: &RecognitionArena,
11847) -> bool {
11848    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
11849    let best_len = arena.diagnostics_len(best_diagnostics);
11850    outcome_position > best_position
11851        || (outcome_position == best_position
11852            && (outcome_len < best_len
11853                || (outcome_len == best_len
11854                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
11855                        < arena.diagnostics_recovery_rank(best_diagnostics))))
11856}
11857
11858fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
11859    if outcomes
11860        .iter()
11861        .any(|outcome| outcome.diagnostics.is_empty())
11862    {
11863        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11864    }
11865}
11866
11867fn discard_recovered_outcomes_if_clean_path_exists(
11868    outcomes: &mut Vec<RecognizeOutcome>,
11869    arena: &RecognitionArena,
11870) {
11871    if outcomes
11872        .iter()
11873        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
11874    {
11875        return;
11876    }
11877    if outcomes
11878        .iter()
11879        .any(|outcome| outcome.diagnostics.is_empty())
11880    {
11881        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11882    }
11883}
11884
11885/// Reports whether a recovered outcome came from an explicit predicate
11886/// fail-option and therefore should compete with shorter clean loop exits.
11887fn outcome_has_rule_failure_diagnostic(
11888    outcome: &RecognizeOutcome,
11889    arena: &RecognitionArena,
11890) -> bool {
11891    arena
11892        .diagnostics(outcome.diagnostics)
11893        .any(|diagnostic| diagnostic.message.starts_with("rule "))
11894}
11895
11896/// Removes equivalent endpoints before memoizing a state result while
11897/// preserving ATN transition-discovery order.
11898///
11899/// Outcomes are compared on observable recognition state — the input index,
11900/// EOF consumption, and diagnostics — without descending into the parse-tree
11901/// fragment carried by `nodes`. Two paths reaching the same point with
11902/// different node trees would otherwise prevent memoization from collapsing
11903/// equivalent suffixes and explode the speculative-path cache.
11904///
11905/// The first occurrence per recognition key wins, which matches ANTLR's
11906/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
11907/// transitions before loop-exit, so the first-discovered outcome carries the
11908/// greedy parse-tree fragment.
11909fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
11910    if outcomes.len() < 2 {
11911        return;
11912    }
11913    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
11914    outcomes.retain(|outcome| {
11915        seen.insert((
11916            outcome.index,
11917            outcome.consumed_eof,
11918            arena.diagnostics_len(outcome.diagnostics),
11919            arena.diagnostics_recovery_rank(outcome.diagnostics),
11920        ))
11921    });
11922}
11923
11924const FAST_OUTCOME_INLINE_KEYS: usize = 8;
11925const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
11926const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
11927const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
11928
11929#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11930enum FastOutcomeDedupStrategy {
11931    Inline,
11932    Dense,
11933    Sparse,
11934}
11935
11936impl FastOutcomeDedupScratch {
11937    fn prepare_dense(&mut self, word_count: usize) {
11938        while let Some(word_index) = self.touched_dense_words.pop() {
11939            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
11940        }
11941        if self.dense_words.len() < word_count {
11942            self.dense_words.resize(word_count, 0);
11943        }
11944    }
11945}
11946
11947fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
11948    let first_index = outcomes.first()?.index;
11949    let (min_index, max_index) = outcomes[1..].iter().fold(
11950        (first_index, first_index),
11951        |(min_index, max_index), outcome| {
11952            (min_index.min(outcome.index), max_index.max(outcome.index))
11953        },
11954    );
11955    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
11956    let bit_count = index_span.checked_mul(2)?;
11957    let word_count =
11958        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
11959    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
11960    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
11961    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
11962        .then_some((min_index, word_count))
11963}
11964
11965#[cfg(feature = "perf-counters")]
11966fn record_clean_fast_outcome_dedup(
11967    strategy: FastOutcomeDedupStrategy,
11968    input_len: usize,
11969    output_len: usize,
11970    dense_words: usize,
11971) {
11972    let counter = match strategy {
11973        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
11974        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
11975        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
11976    };
11977    perf_counters::inc(
11978        &perf_counters::OUTCOME_DEDUPE_INPUTS,
11979        u64::try_from(input_len).unwrap_or(u64::MAX),
11980    );
11981    perf_counters::inc(
11982        &perf_counters::OUTCOME_DEDUPE_REMOVED,
11983        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
11984    );
11985    perf_counters::inc(counter, 1);
11986    perf_counters::inc(
11987        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
11988        u64::try_from(dense_words).unwrap_or(u64::MAX),
11989    );
11990}
11991
11992/// Removes duplicate clean endpoints while preserving transition-discovery
11993/// order. Tiny lists stay on the stack; larger compact ranges use a direct
11994/// bitmap, and only wide sparse ranges pay for hashing.
11995fn dedupe_clean_fast_outcomes(
11996    outcomes: &mut Vec<FastRecognizeOutcome>,
11997    scratch: &mut FastOutcomeDedupScratch,
11998) -> FastOutcomeDedupStrategy {
11999    #[cfg(feature = "perf-counters")]
12000    let input_len = outcomes.len();
12001    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
12002        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
12003        let mut inline_len = 0_usize;
12004        outcomes.retain(|outcome| {
12005            let key = (outcome.index, outcome.consumed_eof);
12006            if inline_keys[..inline_len].contains(&key) {
12007                return false;
12008            }
12009            inline_keys[inline_len] = key;
12010            inline_len += 1;
12011            true
12012        });
12013        #[cfg(feature = "perf-counters")]
12014        record_clean_fast_outcome_dedup(
12015            FastOutcomeDedupStrategy::Inline,
12016            input_len,
12017            outcomes.len(),
12018            0,
12019        );
12020        return FastOutcomeDedupStrategy::Inline;
12021    }
12022
12023    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
12024        scratch.prepare_dense(word_count);
12025        outcomes.retain(|outcome| {
12026            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
12027            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
12028            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
12029            let word = &mut scratch.dense_words[word_index];
12030            if *word & bit != 0 {
12031                return false;
12032            }
12033            if *word == 0 {
12034                scratch
12035                    .touched_dense_words
12036                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
12037            }
12038            *word |= bit;
12039            true
12040        });
12041        #[cfg(feature = "perf-counters")]
12042        record_clean_fast_outcome_dedup(
12043            FastOutcomeDedupStrategy::Dense,
12044            input_len,
12045            outcomes.len(),
12046            word_count,
12047        );
12048        return FastOutcomeDedupStrategy::Dense;
12049    }
12050
12051    scratch.sparse_keys.clear();
12052    scratch.sparse_keys.reserve(outcomes.len());
12053    outcomes.retain(|outcome| {
12054        scratch
12055            .sparse_keys
12056            .insert((outcome.index, outcome.consumed_eof))
12057    });
12058    #[cfg(feature = "perf-counters")]
12059    record_clean_fast_outcome_dedup(
12060        FastOutcomeDedupStrategy::Sparse,
12061        input_len,
12062        outcomes.len(),
12063        0,
12064    );
12065    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
12066        scratch.sparse_keys = FxHashSet::default();
12067    }
12068    FastOutcomeDedupStrategy::Sparse
12069}
12070
12071/// Sorts and removes equivalent endpoints, including action traces and the
12072/// arena-backed node sequence's structural contents.
12073fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
12074    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
12075    outcomes
12076        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
12077}
12078
12079fn compare_recognize_outcomes(
12080    left: &RecognizeOutcome,
12081    right: &RecognizeOutcome,
12082    arena: &RecognitionArena,
12083) -> Ordering {
12084    left.index
12085        .cmp(&right.index)
12086        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
12087        .then_with(|| left.alt_number.cmp(&right.alt_number))
12088        .then_with(|| left.member_values.cmp(&right.member_values))
12089        .then_with(|| left.return_values.cmp(&right.return_values))
12090        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
12091        .then_with(|| left.decisions.cmp(&right.decisions))
12092        .then_with(|| left.actions.cmp(&right.actions))
12093        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
12094}
12095
12096impl<S, H> Recognizer for BaseParser<S, H>
12097where
12098    S: TokenSource,
12099    H: SemanticHooks,
12100{
12101    fn data(&self) -> &RecognizerData {
12102        &self.data
12103    }
12104
12105    fn data_mut(&mut self) -> &mut RecognizerData {
12106        &mut self.data
12107    }
12108}
12109
12110impl<S, H> Parser for BaseParser<S, H>
12111where
12112    S: TokenSource,
12113    H: SemanticHooks,
12114{
12115    fn build_parse_trees(&self) -> bool {
12116        self.build_parse_trees
12117    }
12118
12119    fn set_build_parse_trees(&mut self, build: bool) {
12120        self.build_parse_trees = build;
12121    }
12122
12123    fn number_of_syntax_errors(&self) -> usize {
12124        Self::number_of_syntax_errors(self)
12125    }
12126
12127    fn report_diagnostic_errors(&self) -> bool {
12128        self.report_diagnostic_errors
12129    }
12130
12131    fn set_report_diagnostic_errors(&mut self, report: bool) {
12132        self.report_diagnostic_errors = report;
12133    }
12134
12135    fn prediction_mode(&self) -> PredictionMode {
12136        self.prediction_mode
12137    }
12138
12139    fn set_prediction_mode(&mut self, mode: PredictionMode) {
12140        self.prediction_mode = mode;
12141    }
12142}
12143
12144#[cfg(test)]
12145mod tests {
12146    use super::*;
12147    use crate::atn::parser::{
12148        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
12149    };
12150    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
12151    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
12152    use crate::token_stream::CommonTokenStream;
12153    use crate::tree::{NodeKind, ParseTreeStats};
12154    use crate::vocabulary::Vocabulary;
12155    use std::cell::RefCell;
12156    use std::mem::size_of;
12157    use std::rc::Rc;
12158    use std::sync::{Arc, Mutex};
12159
12160    #[test]
12161    fn fx_hasher_write_matches_typed_methods_for_full_words() {
12162        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
12163        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
12164        // fields) must hash the same as the typed methods, otherwise an
12165        // `FxHashMap` keyed on such a type silently disagrees with itself
12166        // depending on which entry point the caller used. Verify the
12167        // little-endian word equivalence this PR established.
12168        let value: u64 = 0x0102_0304_0506_0708;
12169        let mut typed = FxHasher::default();
12170        typed.write_u64(value);
12171        let mut bytewise = FxHasher::default();
12172        bytewise.write(&value.to_le_bytes());
12173        assert_eq!(typed.finish(), bytewise.finish());
12174    }
12175
12176    #[derive(Clone, Debug)]
12177    struct TestToken {
12178        spec: TokenSpec,
12179        id: TokenId,
12180        source_name: String,
12181    }
12182
12183    impl TestToken {
12184        fn new(token_type: i32) -> Self {
12185            Self {
12186                spec: TokenSpec::explicit(token_type, ""),
12187                id: TokenId::try_from(0).expect("zero token ID"),
12188                source_name: String::new(),
12189            }
12190        }
12191
12192        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
12193            Self {
12194                spec: TokenSpec::eof(index, index, line, column),
12195                id: TokenId::try_from(0).expect("zero token ID"),
12196                source_name: source_name.to_owned(),
12197            }
12198        }
12199
12200        fn with_text(mut self, text: impl Into<String>) -> Self {
12201            self.spec.text = Some(text.into());
12202            self
12203        }
12204
12205        const fn with_channel(mut self, channel: i32) -> Self {
12206            self.spec.channel = channel;
12207            self
12208        }
12209
12210        const fn with_span(mut self, start: usize, stop: usize) -> Self {
12211            self.spec.start = start;
12212            self.spec.stop = stop;
12213            self.spec.start_byte = start;
12214            self.spec.stop_byte = match stop.checked_add(1) {
12215                Some(end) if end >= start => end,
12216                Some(_) | None => start,
12217            };
12218            self
12219        }
12220
12221        const fn with_position(mut self, line: usize, column: usize) -> Self {
12222            self.spec.line = line;
12223            self.spec.column = column;
12224            self
12225        }
12226
12227        fn set_token_index(&mut self, index: isize) {
12228            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
12229        }
12230    }
12231
12232    impl Token for TestToken {
12233        fn token_id(&self) -> TokenId {
12234            self.id
12235        }
12236
12237        fn token_type(&self) -> i32 {
12238            self.spec.token_type
12239        }
12240
12241        fn channel(&self) -> i32 {
12242            self.spec.channel
12243        }
12244
12245        fn start(&self) -> usize {
12246            self.spec.start
12247        }
12248
12249        fn stop(&self) -> usize {
12250            self.spec.stop
12251        }
12252
12253        fn line(&self) -> usize {
12254            self.spec.line
12255        }
12256
12257        fn column(&self) -> usize {
12258            self.spec.column
12259        }
12260
12261        fn text(&self) -> Option<&str> {
12262            self.spec.text.as_deref()
12263        }
12264
12265        fn source_name(&self) -> &str {
12266            &self.source_name
12267        }
12268
12269        fn start_byte(&self) -> usize {
12270            self.spec.start_byte
12271        }
12272
12273        fn stop_byte(&self) -> usize {
12274            self.spec.stop_byte
12275        }
12276    }
12277
12278    #[derive(Debug)]
12279    struct Source {
12280        tokens: Vec<TestToken>,
12281        index: usize,
12282    }
12283
12284    impl TokenSource for Source {
12285        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12286            let token = self
12287                .tokens
12288                .get(self.index)
12289                .cloned()
12290                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
12291            self.index += 1;
12292            sink.push(token.spec)
12293        }
12294
12295        fn line(&self) -> usize {
12296            1
12297        }
12298
12299        fn column(&self) -> usize {
12300            self.index
12301        }
12302
12303        fn source_name(&self) -> &'static str {
12304            "parser-test"
12305        }
12306    }
12307
12308    #[derive(Clone, Debug, Eq, PartialEq)]
12309    struct RecordedDiagnostic {
12310        grammar_file_name: String,
12311        line: usize,
12312        column: usize,
12313        message: String,
12314        error: Option<AntlrError>,
12315    }
12316
12317    #[derive(Clone, Debug)]
12318    struct RecordingErrorListener {
12319        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
12320    }
12321
12322    impl<R> crate::ErrorListener<R> for RecordingErrorListener
12323    where
12324        R: Recognizer + ?Sized,
12325    {
12326        fn syntax_error(
12327            &mut self,
12328            recognizer: &R,
12329            line: usize,
12330            column: usize,
12331            message: &str,
12332            error: Option<&AntlrError>,
12333        ) {
12334            self.diagnostics
12335                .lock()
12336                .expect("recorded diagnostics lock")
12337                .push(RecordedDiagnostic {
12338                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
12339                    line,
12340                    column,
12341                    message: message.to_owned(),
12342                    error: error.cloned(),
12343                });
12344        }
12345    }
12346
12347    #[derive(Debug)]
12348    struct ReportingSource {
12349        source: Source,
12350        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
12351    }
12352
12353    impl TokenSource for ReportingSource {
12354        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12355            self.source.next_token(sink)
12356        }
12357
12358        fn line(&self) -> usize {
12359            self.source.line()
12360        }
12361
12362        fn column(&self) -> usize {
12363            self.source.column()
12364        }
12365
12366        fn source_name(&self) -> &str {
12367            self.source.source_name()
12368        }
12369
12370        fn report_error(&self, error: &TokenSourceError) -> bool {
12371            self.diagnostics.borrow_mut().push(error.clone());
12372            true
12373        }
12374    }
12375
12376    fn mini_parser_data() -> RecognizerData {
12377        RecognizerData::new(
12378            "Mini.g4",
12379            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12380        )
12381        .with_rule_names(["s"])
12382    }
12383
12384    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
12385        let data = mini_parser_data();
12386        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
12387    }
12388
12389    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
12390    where
12391        H: SemanticHooks,
12392    {
12393        BaseParser::with_semantic_hooks(
12394            CommonTokenStream::new(Source { tokens, index: 0 }),
12395            mini_parser_data(),
12396            hooks,
12397        )
12398    }
12399
12400    #[test]
12401    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
12402        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
12403        parser.remove_error_listeners();
12404        let diagnostics = Arc::new(Mutex::new(Vec::new()));
12405        parser.add_error_listener(RecordingErrorListener {
12406            diagnostics: Arc::clone(&diagnostics),
12407        });
12408        let parser_diagnostics = [ParserDiagnostic {
12409            line: 1,
12410            column: 2,
12411            message: "missing 'x' at 'y'".to_owned(),
12412        }];
12413        let token_errors = [
12414            TokenSourceError::new(1, 1, "token recognition error at: '@'"),
12415            TokenSourceError::new(1, 3, "token recognition error at: '#'"),
12416        ];
12417
12418        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12419
12420        assert_eq!(
12421            *diagnostics.lock().expect("recorded diagnostics lock"),
12422            [
12423                RecordedDiagnostic {
12424                    grammar_file_name: "Mini.g4".to_owned(),
12425                    line: 1,
12426                    column: 1,
12427                    message: "token recognition error at: '@'".to_owned(),
12428                    error: None,
12429                },
12430                RecordedDiagnostic {
12431                    grammar_file_name: "Mini.g4".to_owned(),
12432                    line: 1,
12433                    column: 2,
12434                    message: "missing 'x' at 'y'".to_owned(),
12435                    error: None,
12436                },
12437                RecordedDiagnostic {
12438                    grammar_file_name: "Mini.g4".to_owned(),
12439                    line: 1,
12440                    column: 3,
12441                    message: "token recognition error at: '#'".to_owned(),
12442                    error: None,
12443                },
12444            ]
12445        );
12446
12447        parser.remove_error_listeners();
12448        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12449        assert_eq!(
12450            diagnostics.lock().expect("recorded diagnostics lock").len(),
12451            3
12452        );
12453    }
12454
12455    #[test]
12456    fn parser_leaves_token_errors_to_source_owned_listeners() {
12457        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
12458        let source = ReportingSource {
12459            source: Source {
12460                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
12461                index: 0,
12462            },
12463            diagnostics: Rc::clone(&source_diagnostics),
12464        };
12465        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
12466        parser.remove_error_listeners();
12467        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
12468        parser.add_error_listener(RecordingErrorListener {
12469            diagnostics: Arc::clone(&parser_diagnostics),
12470        });
12471        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
12472
12473        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
12474
12475        assert_eq!(*source_diagnostics.borrow(), [source_error]);
12476        assert!(
12477            parser_diagnostics
12478                .lock()
12479                .expect("recorded diagnostics lock")
12480                .is_empty()
12481        );
12482    }
12483
12484    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
12485        builder.finish().expect("valid packed parser ATN")
12486    }
12487
12488    fn nested_rule_chain_atn(depth: usize) -> Atn {
12489        nested_rule_graph_atn(depth, false, false)
12490    }
12491
12492    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
12493        assert!(depth > 0);
12494        let mut atn = ParserAtnBuilder::new(2);
12495        let mut starts = Vec::with_capacity(depth);
12496        let mut stops = Vec::with_capacity(depth);
12497        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
12498        for rule_index in 0..depth {
12499            starts.push(
12500                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
12501                    .expect("rule start")
12502                    .index(),
12503            );
12504        }
12505        for rule_index in 0..depth {
12506            stops.push(
12507                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
12508                    .expect("rule stop")
12509                    .index(),
12510            );
12511        }
12512        if consuming_follows {
12513            for rule_index in 0..depth - 1 {
12514                follows.push(
12515                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
12516                        .expect("rule follow")
12517                        .index(),
12518                );
12519            }
12520        }
12521        atn.set_rule_to_start_state(starts.clone())
12522            .expect("rule start states");
12523        atn.set_rule_to_stop_state(stops.clone())
12524            .expect("rule stop states");
12525        for rule_index in 0..depth - 1 {
12526            let follow_state = if consuming_follows {
12527                follows[rule_index]
12528            } else {
12529                stops[rule_index]
12530            };
12531            atn.add_transition(
12532                starts[rule_index],
12533                ParserTransitionSpec::Rule {
12534                    target: starts[rule_index + 1],
12535                    rule_index: rule_index + 1,
12536                    follow_state,
12537                    precedence: 0,
12538                },
12539            )
12540            .expect("nested rule transition");
12541            if branching {
12542                atn.add_transition(
12543                    starts[rule_index],
12544                    ParserTransitionSpec::Atom {
12545                        target: stops[rule_index],
12546                        label: 2,
12547                    },
12548                )
12549                .expect("dead branch transition");
12550            }
12551            if consuming_follows {
12552                atn.add_transition(
12553                    follow_state,
12554                    ParserTransitionSpec::Atom {
12555                        target: stops[rule_index],
12556                        label: 1,
12557                    },
12558                )
12559                .expect("consuming follow transition");
12560            }
12561        }
12562        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
12563        atn.add_transition(
12564            starts[depth - 1],
12565            ParserTransitionSpec::Set {
12566                target: stops[depth - 1],
12567                set: token_set,
12568            },
12569        )
12570        .expect("terminal set transition");
12571        if branching {
12572            atn.add_transition(
12573                starts[depth - 1],
12574                ParserTransitionSpec::Atom {
12575                    target: stops[depth - 1],
12576                    label: 2,
12577                },
12578            )
12579            .expect("dead leaf branch transition");
12580        }
12581        finish_atn(atn)
12582    }
12583
12584    fn ordinary_star_loop_atn() -> Atn {
12585        let mut atn = ParserAtnBuilder::new(2);
12586        for (state_number, kind, rule_index) in [
12587            (0, AtnStateKind::RuleStart, 0),
12588            (1, AtnStateKind::StarLoopEntry, 0),
12589            (2, AtnStateKind::Basic, 0),
12590            (3, AtnStateKind::StarLoopBack, 0),
12591            (4, AtnStateKind::LoopEnd, 0),
12592            (5, AtnStateKind::Basic, 0),
12593            (6, AtnStateKind::RuleStop, 0),
12594            (7, AtnStateKind::RuleStart, 1),
12595            (8, AtnStateKind::Basic, 1),
12596            (9, AtnStateKind::RuleStop, 1),
12597        ] {
12598            assert_eq!(
12599                atn.add_state(kind, Some(rule_index))
12600                    .expect("state")
12601                    .index(),
12602                state_number
12603            );
12604        }
12605        atn.set_rule_to_start_state(vec![0, 7])
12606            .expect("rule start states");
12607        atn.set_rule_to_stop_state(vec![6, 9])
12608            .expect("rule stop states");
12609        atn.add_decision_state(1).expect("decision state");
12610        atn.set_loop_back_state(4, 3).expect("loop back state");
12611        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12612            .expect("transition");
12613        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12614            .expect("transition");
12615        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12616            .expect("transition");
12617        atn.add_transition(
12618            2,
12619            ParserTransitionSpec::Rule {
12620                target: 7,
12621                rule_index: 1,
12622                follow_state: 3,
12623                precedence: 0,
12624            },
12625        )
12626        .expect("transition");
12627        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12628            .expect("transition");
12629        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12630            .expect("transition");
12631        atn.add_transition(
12632            5,
12633            ParserTransitionSpec::Atom {
12634                target: 6,
12635                label: TOKEN_EOF,
12636            },
12637        )
12638        .expect("transition");
12639        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12640            .expect("transition");
12641        atn.add_transition(
12642            8,
12643            ParserTransitionSpec::Atom {
12644                target: 9,
12645                label: 1,
12646            },
12647        )
12648        .expect("transition");
12649        finish_atn(atn)
12650    }
12651
12652    /// ATN for `s : (X | X X)* EOF`.
12653    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12654        let mut atn = ParserAtnBuilder::new(1);
12655        for (state_number, kind) in [
12656            (0, AtnStateKind::RuleStart),
12657            (1, AtnStateKind::StarLoopEntry),
12658            (2, AtnStateKind::StarBlockStart),
12659            (3, AtnStateKind::Basic),
12660            (4, AtnStateKind::BlockEnd),
12661            (5, AtnStateKind::StarLoopBack),
12662            (6, AtnStateKind::LoopEnd),
12663            (7, AtnStateKind::Basic),
12664            (8, AtnStateKind::RuleStop),
12665        ] {
12666            assert_eq!(
12667                atn.add_state(kind, Some(0)).expect("state").index(),
12668                state_number
12669            );
12670        }
12671        atn.set_rule_to_start_state(vec![0])
12672            .expect("rule start states");
12673        atn.set_rule_to_stop_state(vec![8])
12674            .expect("rule stop states");
12675        atn.set_end_state(2, 4).expect("block end state");
12676        atn.set_loop_back_state(6, 5).expect("loop back state");
12677        atn.add_decision_state(1).expect("decision state");
12678        atn.add_decision_state(2).expect("decision state");
12679        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12680            .expect("transition");
12681        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12682            .expect("transition");
12683        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
12684            .expect("transition");
12685        atn.add_transition(
12686            2,
12687            ParserTransitionSpec::Atom {
12688                target: 4,
12689                label: 1,
12690            },
12691        )
12692        .expect("transition");
12693        atn.add_transition(
12694            2,
12695            ParserTransitionSpec::Atom {
12696                target: 3,
12697                label: 1,
12698            },
12699        )
12700        .expect("transition");
12701        atn.add_transition(
12702            3,
12703            ParserTransitionSpec::Atom {
12704                target: 4,
12705                label: 1,
12706            },
12707        )
12708        .expect("transition");
12709        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12710            .expect("transition");
12711        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
12712            .expect("transition");
12713        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12714            .expect("transition");
12715        atn.add_transition(
12716            7,
12717            ParserTransitionSpec::Atom {
12718                target: 8,
12719                label: TOKEN_EOF,
12720            },
12721        )
12722        .expect("transition");
12723        finish_atn(atn)
12724    }
12725
12726    fn ordinary_plus_loop_atn() -> Atn {
12727        let mut atn = ParserAtnBuilder::new(2);
12728        for (state_number, kind, rule_index) in [
12729            (0, AtnStateKind::RuleStart, 0),
12730            (1, AtnStateKind::Basic, 0),
12731            (2, AtnStateKind::PlusLoopBack, 0),
12732            (3, AtnStateKind::LoopEnd, 0),
12733            (4, AtnStateKind::Basic, 0),
12734            (5, AtnStateKind::RuleStop, 0),
12735            (6, AtnStateKind::RuleStart, 1),
12736            (7, AtnStateKind::Basic, 1),
12737            (8, AtnStateKind::RuleStop, 1),
12738        ] {
12739            assert_eq!(
12740                atn.add_state(kind, Some(rule_index))
12741                    .expect("state")
12742                    .index(),
12743                state_number
12744            );
12745        }
12746        atn.set_rule_to_start_state(vec![0, 6])
12747            .expect("rule start states");
12748        atn.set_rule_to_stop_state(vec![5, 8])
12749            .expect("rule stop states");
12750        atn.add_decision_state(2).expect("decision state");
12751        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12752            .expect("transition");
12753        atn.add_transition(
12754            1,
12755            ParserTransitionSpec::Rule {
12756                target: 6,
12757                rule_index: 1,
12758                follow_state: 2,
12759                precedence: 0,
12760            },
12761        )
12762        .expect("transition");
12763        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
12764            .expect("transition");
12765        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12766            .expect("transition");
12767        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
12768            .expect("transition");
12769        atn.add_transition(
12770            4,
12771            ParserTransitionSpec::Atom {
12772                target: 5,
12773                label: TOKEN_EOF,
12774            },
12775        )
12776        .expect("transition");
12777        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12778            .expect("transition");
12779        atn.add_transition(
12780            7,
12781            ParserTransitionSpec::Atom {
12782                target: 8,
12783                label: 1,
12784            },
12785        )
12786        .expect("transition");
12787        finish_atn(atn)
12788    }
12789
12790    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
12791        let mut tokens = (0..count)
12792            .map(|_| TestToken::new(1).with_text("x"))
12793            .collect::<Vec<_>>();
12794        tokens.push(TestToken::eof("parser-test", count, 1, count));
12795        tokens
12796    }
12797
12798    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
12799        let mut atn = ParserAtnBuilder::new(2);
12800        assert_eq!(
12801            atn.add_state(AtnStateKind::RuleStart, Some(0))
12802                .expect("state")
12803                .index(),
12804            0
12805        );
12806        assert_eq!(
12807            atn.add_state(AtnStateKind::Basic, Some(0))
12808                .expect("state")
12809                .index(),
12810            1
12811        );
12812        assert_eq!(
12813            atn.add_state(AtnStateKind::Basic, Some(0))
12814                .expect("state")
12815                .index(),
12816            2
12817        );
12818        assert_eq!(
12819            atn.add_state(AtnStateKind::RuleStart, Some(1))
12820                .expect("state")
12821                .index(),
12822            3
12823        );
12824        atn.set_left_recursive_rule(3)
12825            .expect("left-recursive rule start");
12826        assert_eq!(
12827            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
12828                .expect("state")
12829                .index(),
12830            4
12831        );
12832        atn.set_precedence_rule_decision(4)
12833            .expect("precedence decision");
12834        assert_eq!(
12835            atn.add_state(AtnStateKind::Basic, Some(1))
12836                .expect("state")
12837                .index(),
12838            5
12839        );
12840        assert_eq!(
12841            atn.add_state(AtnStateKind::Basic, Some(1))
12842                .expect("state")
12843                .index(),
12844            6
12845        );
12846        assert_eq!(
12847            atn.add_state(AtnStateKind::LoopEnd, Some(1))
12848                .expect("state")
12849                .index(),
12850            7
12851        );
12852        assert_eq!(
12853            atn.add_state(AtnStateKind::RuleStop, Some(1))
12854                .expect("state")
12855                .index(),
12856            8
12857        );
12858        assert_eq!(
12859            atn.add_state(AtnStateKind::RuleStop, Some(0))
12860                .expect("state")
12861                .index(),
12862            9
12863        );
12864        atn.set_rule_to_start_state(vec![0, 3])
12865            .expect("rule start states");
12866        atn.set_rule_to_stop_state(vec![9, 8])
12867            .expect("rule stop states");
12868        atn.add_transition(
12869            1,
12870            ParserTransitionSpec::Rule {
12871                target: 3,
12872                rule_index: 1,
12873                follow_state: 2,
12874                precedence: 0,
12875            },
12876        )
12877        .expect("transition");
12878        atn.add_transition(
12879            2,
12880            ParserTransitionSpec::Atom {
12881                target: 9,
12882                label: caller_symbol,
12883            },
12884        )
12885        .expect("transition");
12886        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12887            .expect("transition");
12888        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
12889            .expect("transition");
12890        atn.add_transition(
12891            5,
12892            ParserTransitionSpec::Precedence {
12893                target: 6,
12894                precedence: 1,
12895            },
12896        )
12897        .expect("transition");
12898        atn.add_transition(
12899            6,
12900            ParserTransitionSpec::Atom {
12901                target: 4,
12902                label: 1,
12903            },
12904        )
12905        .expect("transition");
12906        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12907            .expect("transition");
12908        finish_atn(atn)
12909    }
12910
12911    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
12912        let mut parser = mini_parser(vec![
12913            TestToken::new(symbol).with_text("lookahead"),
12914            TestToken::eof("parser-test", 1, 1, 1),
12915        ]);
12916        parser.rule_context_stack = vec![
12917            RuleContextFrame {
12918                rule_index: 0,
12919                invoking_state: -1,
12920            },
12921            RuleContextFrame {
12922                rule_index: 1,
12923                invoking_state: 1,
12924            },
12925        ];
12926        parser
12927    }
12928
12929    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
12930        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
12931        //   prec 2: token 1, token 1  (shift `>>`)
12932        //   prec 1: token 1           (relational `>`)
12933        let mut atn = ParserAtnBuilder::new(1);
12934        for (state, kind, rule) in [
12935            (0, AtnStateKind::RuleStart, 0),
12936            (1, AtnStateKind::StarLoopEntry, 0),
12937            (2, AtnStateKind::Basic, 0), // ops hub
12938            (3, AtnStateKind::Basic, 0), // shift prec
12939            (4, AtnStateKind::Basic, 0), // shift first >
12940            (5, AtnStateKind::Basic, 0), // shift second >
12941            (6, AtnStateKind::Basic, 0), // rel prec
12942            (7, AtnStateKind::Basic, 0), // rel >
12943            (8, AtnStateKind::LoopEnd, 0),
12944            (9, AtnStateKind::RuleStop, 0),
12945        ] {
12946            assert_eq!(
12947                atn.add_state(kind, Some(rule)).expect("state").index(),
12948                state
12949            );
12950            if state == 0 {
12951                atn.set_left_recursive_rule(state)
12952                    .expect("left-recursive rule start");
12953            } else if state == 1 {
12954                atn.set_precedence_rule_decision(state)
12955                    .expect("precedence decision");
12956            }
12957        }
12958        atn.set_rule_to_start_state(vec![0])
12959            .expect("rule start states");
12960        atn.set_rule_to_stop_state(vec![9])
12961            .expect("rule stop states");
12962        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12963            .expect("ops");
12964        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12965            .expect("exit");
12966        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12967            .expect("to shift");
12968        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12969            .expect("to rel");
12970        atn.add_transition(
12971            3,
12972            ParserTransitionSpec::Precedence {
12973                target: 4,
12974                precedence: 2,
12975            },
12976        )
12977        .expect("shift prec");
12978        atn.add_transition(
12979            4,
12980            ParserTransitionSpec::Atom {
12981                target: 5,
12982                label: 1,
12983            },
12984        )
12985        .expect("shift first >");
12986        atn.add_transition(
12987            5,
12988            ParserTransitionSpec::Atom {
12989                target: 1,
12990                label: 1,
12991            },
12992        )
12993        .expect("shift second >");
12994        atn.add_transition(
12995            6,
12996            ParserTransitionSpec::Precedence {
12997                target: 7,
12998                precedence: 1,
12999            },
13000        )
13001        .expect("rel prec");
13002        atn.add_transition(
13003            7,
13004            ParserTransitionSpec::Atom {
13005                target: 1,
13006                label: 1,
13007            },
13008        )
13009        .expect("rel >");
13010        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13011            .expect("loop end");
13012        finish_atn(atn)
13013    }
13014
13015    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
13016        let mut atn = ParserAtnBuilder::new(2);
13017        for (state, kind, rule) in [
13018            (0, AtnStateKind::RuleStart, 0),
13019            (1, AtnStateKind::StarLoopEntry, 0),
13020            (2, AtnStateKind::Basic, 0),
13021            (3, AtnStateKind::Basic, 0),
13022            (4, AtnStateKind::Basic, 0),
13023            (5, AtnStateKind::Basic, 0),
13024            (6, AtnStateKind::Basic, 0),
13025            (7, AtnStateKind::Basic, 0),
13026            (8, AtnStateKind::LoopEnd, 0),
13027            (9, AtnStateKind::RuleStop, 0),
13028            (10, AtnStateKind::RuleStart, 1),
13029            (11, AtnStateKind::Basic, 1),
13030            (12, AtnStateKind::RuleStop, 1),
13031        ] {
13032            assert_eq!(
13033                atn.add_state(kind, Some(rule)).expect("state").index(),
13034                state
13035            );
13036            if state == 0 {
13037                atn.set_left_recursive_rule(state)
13038                    .expect("left-recursive rule start");
13039            } else if state == 1 {
13040                atn.set_precedence_rule_decision(state)
13041                    .expect("precedence decision");
13042            }
13043        }
13044        atn.set_rule_to_start_state(vec![0, 10])
13045            .expect("rule start states");
13046        atn.set_rule_to_stop_state(vec![9, 12])
13047            .expect("rule stop states");
13048        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13049            .expect("ops");
13050        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13051            .expect("exit");
13052        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13053            .expect("to shift");
13054        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13055            .expect("to relational");
13056        atn.add_transition(
13057            3,
13058            ParserTransitionSpec::Precedence {
13059                target: 4,
13060                precedence: 2,
13061            },
13062        )
13063        .expect("shift precedence");
13064        atn.add_transition(
13065            4,
13066            ParserTransitionSpec::Rule {
13067                target: 10,
13068                rule_index: 1,
13069                follow_state: 5,
13070                precedence: 0,
13071            },
13072        )
13073        .expect("first shift token helper");
13074        atn.add_transition(
13075            5,
13076            ParserTransitionSpec::Atom {
13077                target: 1,
13078                label: 1,
13079            },
13080        )
13081        .expect("second shift token");
13082        atn.add_transition(
13083            6,
13084            ParserTransitionSpec::Precedence {
13085                target: 7,
13086                precedence: 1,
13087            },
13088        )
13089        .expect("relational precedence");
13090        atn.add_transition(
13091            7,
13092            ParserTransitionSpec::Atom {
13093                target: 1,
13094                label: 1,
13095            },
13096        )
13097        .expect("relational token");
13098        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13099            .expect("loop end");
13100        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13101            .expect("helper entry");
13102        atn.add_transition(
13103            11,
13104            ParserTransitionSpec::Atom {
13105                target: 12,
13106                label: 1,
13107            },
13108        )
13109        .expect("first shift token");
13110        finish_atn(atn)
13111    }
13112
13113    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
13114        let mut atn = ParserAtnBuilder::new(1);
13115        for (state, kind) in [
13116            (0, AtnStateKind::RuleStart),
13117            (1, AtnStateKind::StarLoopEntry),
13118            (2, AtnStateKind::Basic),
13119            (3, AtnStateKind::Basic),
13120            (4, AtnStateKind::Basic),
13121            (5, AtnStateKind::Basic),
13122            (6, AtnStateKind::Basic),
13123            (7, AtnStateKind::Basic),
13124            (8, AtnStateKind::Basic),
13125            (9, AtnStateKind::LoopEnd),
13126            (10, AtnStateKind::RuleStop),
13127        ] {
13128            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13129            if state == 0 {
13130                atn.set_left_recursive_rule(state)
13131                    .expect("left-recursive rule start");
13132            } else if state == 1 {
13133                atn.set_precedence_rule_decision(state)
13134                    .expect("precedence decision");
13135            }
13136        }
13137        atn.set_rule_to_start_state(vec![0])
13138            .expect("rule start states");
13139        atn.set_rule_to_stop_state(vec![10])
13140            .expect("rule stop states");
13141        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13142            .expect("ops");
13143        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
13144            .expect("exit");
13145        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13146            .expect("to multi-token operator");
13147        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13148            .expect("to predicate operator");
13149        atn.add_transition(
13150            3,
13151            ParserTransitionSpec::Precedence {
13152                target: 4,
13153                precedence: 2,
13154            },
13155        )
13156        .expect("multi-token precedence");
13157        atn.add_transition(
13158            4,
13159            ParserTransitionSpec::Atom {
13160                target: 5,
13161                label: 1,
13162            },
13163        )
13164        .expect("multi-token first");
13165        atn.add_transition(
13166            5,
13167            ParserTransitionSpec::Atom {
13168                target: 1,
13169                label: 1,
13170            },
13171        )
13172        .expect("multi-token second");
13173        atn.add_transition(
13174            6,
13175            ParserTransitionSpec::Precedence {
13176                target: 7,
13177                precedence: 2,
13178            },
13179        )
13180        .expect("predicate precedence");
13181        atn.add_transition(
13182            7,
13183            ParserTransitionSpec::Predicate {
13184                target: 8,
13185                rule_index: 0,
13186                pred_index: 0,
13187                context_dependent: false,
13188            },
13189        )
13190        .expect("operator predicate");
13191        atn.add_transition(
13192            8,
13193            ParserTransitionSpec::Atom {
13194                target: 1,
13195                label: 1,
13196            },
13197        )
13198        .expect("predicate single token");
13199        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13200            .expect("loop end");
13201        finish_atn(atn)
13202    }
13203
13204    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
13205        let mut atn = ParserAtnBuilder::new(2);
13206        for (state, kind, rule) in [
13207            (0, AtnStateKind::RuleStart, 0),
13208            (1, AtnStateKind::StarLoopEntry, 0),
13209            (2, AtnStateKind::Basic, 0),
13210            (3, AtnStateKind::Basic, 0),
13211            (4, AtnStateKind::Basic, 0),
13212            (5, AtnStateKind::LoopEnd, 0),
13213            (6, AtnStateKind::RuleStop, 0),
13214            (7, AtnStateKind::RuleStart, 1),
13215            (8, AtnStateKind::RuleStop, 1),
13216            (9, AtnStateKind::Basic, 1),
13217        ] {
13218            assert_eq!(
13219                atn.add_state(kind, Some(rule)).expect("state").index(),
13220                state
13221            );
13222            if state == 0 {
13223                atn.set_left_recursive_rule(state)
13224                    .expect("left-recursive rule start");
13225            } else if state == 1 {
13226                atn.set_precedence_rule_decision(state)
13227                    .expect("precedence decision");
13228            }
13229        }
13230        atn.set_rule_to_start_state(vec![0, 7])
13231            .expect("rule start states");
13232        atn.set_rule_to_stop_state(vec![6, 8])
13233            .expect("rule stop states");
13234        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13235            .expect("transition");
13236        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13237            .expect("transition");
13238        atn.add_transition(
13239            2,
13240            ParserTransitionSpec::Precedence {
13241                target: 3,
13242                precedence: 3,
13243            },
13244        )
13245        .expect("transition");
13246        atn.add_transition(
13247            3,
13248            ParserTransitionSpec::Rule {
13249                target: 7,
13250                rule_index: 1,
13251                follow_state: 4,
13252                precedence: 0,
13253            },
13254        )
13255        .expect("transition");
13256        atn.add_transition(
13257            4,
13258            ParserTransitionSpec::Atom {
13259                target: 1,
13260                label: 1,
13261            },
13262        )
13263        .expect("transition");
13264        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13265            .expect("transition");
13266        atn.add_transition(
13267            7,
13268            ParserTransitionSpec::Precedence {
13269                target: 9,
13270                precedence: 1,
13271            },
13272        )
13273        .expect("transition");
13274        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
13275            .expect("transition");
13276        finish_atn(atn)
13277    }
13278
13279    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
13280        let mut atn = ParserAtnBuilder::new(2);
13281        for (state, kind) in [
13282            (0, AtnStateKind::RuleStart),
13283            (1, AtnStateKind::StarLoopEntry),
13284            (2, AtnStateKind::Basic),
13285            (3, AtnStateKind::Basic),
13286            (4, AtnStateKind::Basic),
13287            (5, AtnStateKind::LoopEnd),
13288            (6, AtnStateKind::RuleStop),
13289        ] {
13290            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13291            if state == 0 {
13292                atn.set_left_recursive_rule(state)
13293                    .expect("left-recursive rule start");
13294            } else if state == 1 {
13295                atn.set_precedence_rule_decision(state)
13296                    .expect("precedence decision");
13297            }
13298        }
13299        atn.set_rule_to_start_state(vec![0])
13300            .expect("rule start states");
13301        atn.set_rule_to_stop_state(vec![6])
13302            .expect("rule stop states");
13303        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13304            .expect("transition");
13305        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13306            .expect("transition");
13307        atn.add_transition(
13308            2,
13309            ParserTransitionSpec::Precedence {
13310                target: 3,
13311                precedence: 1,
13312            },
13313        )
13314        .expect("transition");
13315        atn.add_transition(
13316            3,
13317            ParserTransitionSpec::Predicate {
13318                target: 4,
13319                rule_index: 0,
13320                pred_index: 0,
13321                context_dependent: false,
13322            },
13323        )
13324        .expect("transition");
13325        atn.add_transition(
13326            4,
13327            ParserTransitionSpec::Atom {
13328                target: 1,
13329                label: 1,
13330            },
13331        )
13332        .expect("transition");
13333        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13334            .expect("transition");
13335        finish_atn(atn)
13336    }
13337
13338    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
13339        let mut atn = ParserAtnBuilder::new(2);
13340        for (state, kind, rule) in [
13341            (0, AtnStateKind::RuleStart, 0),
13342            (1, AtnStateKind::Basic, 0),
13343            (2, AtnStateKind::Basic, 0),
13344            (3, AtnStateKind::Basic, 0),
13345            (4, AtnStateKind::RuleStop, 0),
13346            (5, AtnStateKind::RuleStart, 1),
13347            (6, AtnStateKind::StarLoopEntry, 1),
13348            (7, AtnStateKind::Basic, 1),
13349            (8, AtnStateKind::Basic, 1),
13350            (9, AtnStateKind::LoopEnd, 1),
13351            (10, AtnStateKind::RuleStop, 1),
13352            (11, AtnStateKind::RuleStart, 2),
13353            (12, AtnStateKind::RuleStop, 2),
13354        ] {
13355            assert_eq!(
13356                atn.add_state(kind, Some(rule)).expect("state").index(),
13357                state
13358            );
13359            if state == 5 {
13360                atn.set_left_recursive_rule(state)
13361                    .expect("left-recursive rule start");
13362            } else if state == 6 {
13363                atn.set_precedence_rule_decision(state)
13364                    .expect("precedence decision");
13365            }
13366        }
13367        atn.set_rule_to_start_state(vec![0, 5, 11])
13368            .expect("rule start states");
13369        atn.set_rule_to_stop_state(vec![4, 10, 12])
13370            .expect("rule stop states");
13371        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13372            .expect("transition");
13373        atn.add_transition(
13374            1,
13375            ParserTransitionSpec::Rule {
13376                target: 5,
13377                rule_index: 1,
13378                follow_state: 2,
13379                precedence: 0,
13380            },
13381        )
13382        .expect("transition");
13383        atn.add_transition(
13384            2,
13385            ParserTransitionSpec::Rule {
13386                target: 11,
13387                rule_index: 2,
13388                follow_state: 3,
13389                precedence: 0,
13390            },
13391        )
13392        .expect("transition");
13393        atn.add_transition(
13394            3,
13395            ParserTransitionSpec::Atom {
13396                target: 4,
13397                label: caller_symbol,
13398            },
13399        )
13400        .expect("transition");
13401        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13402            .expect("transition");
13403        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
13404            .expect("transition");
13405        atn.add_transition(
13406            7,
13407            ParserTransitionSpec::Precedence {
13408                target: 8,
13409                precedence: 1,
13410            },
13411        )
13412        .expect("transition");
13413        atn.add_transition(
13414            8,
13415            ParserTransitionSpec::Atom {
13416                target: 6,
13417                label: 1,
13418            },
13419        )
13420        .expect("transition");
13421        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13422            .expect("transition");
13423        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
13424            .expect("transition");
13425        finish_atn(atn)
13426    }
13427
13428    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
13429        let mut atn = ParserAtnBuilder::new(2);
13430        for (state, kind, rule) in [
13431            (0, AtnStateKind::RuleStart, 0),
13432            (1, AtnStateKind::Basic, 0),
13433            (2, AtnStateKind::Basic, 0),
13434            (3, AtnStateKind::RuleStop, 0),
13435            (4, AtnStateKind::RuleStart, 1),
13436            (5, AtnStateKind::Basic, 1),
13437            (6, AtnStateKind::Basic, 1),
13438            (7, AtnStateKind::RuleStop, 1),
13439            (8, AtnStateKind::RuleStart, 2),
13440            (9, AtnStateKind::StarLoopEntry, 2),
13441            (10, AtnStateKind::Basic, 2),
13442            (11, AtnStateKind::Basic, 2),
13443            (12, AtnStateKind::LoopEnd, 2),
13444            (13, AtnStateKind::RuleStop, 2),
13445        ] {
13446            assert_eq!(
13447                atn.add_state(kind, Some(rule)).expect("state").index(),
13448                state
13449            );
13450            if state == 8 {
13451                atn.set_left_recursive_rule(state)
13452                    .expect("left-recursive rule start");
13453            } else if state == 9 {
13454                atn.set_precedence_rule_decision(state)
13455                    .expect("precedence decision");
13456            }
13457        }
13458        atn.set_rule_to_start_state(vec![0, 4, 8])
13459            .expect("rule start states");
13460        atn.set_rule_to_stop_state(vec![3, 7, 13])
13461            .expect("rule stop states");
13462        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13463            .expect("transition");
13464        atn.add_transition(
13465            1,
13466            ParserTransitionSpec::Rule {
13467                target: 4,
13468                rule_index: 1,
13469                follow_state: 2,
13470                precedence: 0,
13471            },
13472        )
13473        .expect("transition");
13474        atn.add_transition(
13475            2,
13476            ParserTransitionSpec::Atom {
13477                target: 3,
13478                label: caller_symbol,
13479            },
13480        )
13481        .expect("transition");
13482        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13483            .expect("transition");
13484        atn.add_transition(
13485            5,
13486            ParserTransitionSpec::Rule {
13487                target: 8,
13488                rule_index: 2,
13489                follow_state: 6,
13490                precedence: 0,
13491            },
13492        )
13493        .expect("transition");
13494        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13495            .expect("transition");
13496        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13497            .expect("transition");
13498        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
13499            .expect("transition");
13500        atn.add_transition(
13501            10,
13502            ParserTransitionSpec::Precedence {
13503                target: 11,
13504                precedence: 1,
13505            },
13506        )
13507        .expect("transition");
13508        atn.add_transition(
13509            11,
13510            ParserTransitionSpec::Atom {
13511                target: 9,
13512                label: 1,
13513            },
13514        )
13515        .expect("transition");
13516        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
13517            .expect("transition");
13518        finish_atn(atn)
13519    }
13520
13521    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
13522        let mut atn = ParserAtnBuilder::new(2);
13523        for (state, kind, rule) in [
13524            (0, AtnStateKind::RuleStart, 0),
13525            (1, AtnStateKind::Basic, 0),
13526            (2, AtnStateKind::Basic, 0),
13527            (3, AtnStateKind::RuleStop, 0),
13528            (4, AtnStateKind::RuleStart, 1),
13529            (5, AtnStateKind::StarLoopEntry, 1),
13530            (6, AtnStateKind::Basic, 1),
13531            (7, AtnStateKind::Basic, 1),
13532            (8, AtnStateKind::Basic, 1),
13533            (9, AtnStateKind::Basic, 1),
13534            (10, AtnStateKind::LoopEnd, 1),
13535            (11, AtnStateKind::RuleStop, 1),
13536        ] {
13537            assert_eq!(
13538                atn.add_state(kind, Some(rule)).expect("state").index(),
13539                state
13540            );
13541            if state == 4 {
13542                atn.set_left_recursive_rule(state)
13543                    .expect("left-recursive rule start");
13544            } else if state == 5 {
13545                atn.set_precedence_rule_decision(state)
13546                    .expect("precedence decision");
13547            }
13548        }
13549        atn.set_rule_to_start_state(vec![0, 4])
13550            .expect("rule start states");
13551        atn.set_rule_to_stop_state(vec![3, 11])
13552            .expect("rule stop states");
13553        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13554            .expect("transition");
13555        atn.add_transition(
13556            1,
13557            ParserTransitionSpec::Rule {
13558                target: 4,
13559                rule_index: 1,
13560                follow_state: 2,
13561                precedence: 0,
13562            },
13563        )
13564        .expect("transition");
13565        atn.add_transition(
13566            2,
13567            ParserTransitionSpec::Atom {
13568                target: 3,
13569                label: caller_symbol,
13570            },
13571        )
13572        .expect("transition");
13573        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13574            .expect("transition");
13575        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
13576            .expect("transition");
13577        atn.add_transition(
13578            6,
13579            ParserTransitionSpec::Precedence {
13580                target: 7,
13581                precedence: 1,
13582            },
13583        )
13584        .expect("transition");
13585        atn.add_transition(
13586            7,
13587            ParserTransitionSpec::Atom {
13588                target: 8,
13589                label: 1,
13590            },
13591        )
13592        .expect("transition");
13593        atn.add_transition(
13594            8,
13595            ParserTransitionSpec::Rule {
13596                target: 4,
13597                rule_index: 1,
13598                follow_state: 9,
13599                precedence: 2,
13600            },
13601        )
13602        .expect("transition");
13603        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13604            .expect("transition");
13605        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13606            .expect("transition");
13607        finish_atn(atn)
13608    }
13609
13610    #[test]
13611    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13612        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13613        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13614
13615        let mut overlapping = parser_inside_left_recursive_callee(1);
13616        assert_eq!(
13617            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13618            None
13619        );
13620
13621        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13622        assert_eq!(
13623            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13624            Some(true)
13625        );
13626
13627        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13628        assert_eq!(
13629            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13630            Some(false)
13631        );
13632
13633        assert_eq!(
13634            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13635            Some(true),
13636            "overlap results must not leak across ATNs"
13637        );
13638    }
13639
13640    #[test]
13641    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
13642        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
13643        let mut parser = mini_parser(vec![
13644            TestToken::new(1).with_text("operator"),
13645            TestToken::eof("parser-test", 1, 1, 1),
13646        ]);
13647        parser.rule_context_stack = vec![RuleContextFrame {
13648            rule_index: 0,
13649            invoking_state: -1,
13650        }];
13651
13652        assert_eq!(
13653            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13654            Some(true)
13655        );
13656        assert_eq!(
13657            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13658            Some(true),
13659            "cached operator lookahead must preserve the nullable prefix return path"
13660        );
13661        assert_eq!(
13662            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13663            Some(true),
13664            "the nullable child must use its rule-call precedence, not the caller precedence"
13665        );
13666    }
13667
13668    #[test]
13669    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
13670        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
13671        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
13672        // must defer so StarLoopEntry adaptive predict can exit when the second
13673        // `>` is absent (as in `a < b > c`).
13674        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
13675        let mut parser = mini_parser(vec![
13676            TestToken::new(1).with_text(">"),
13677            TestToken::new(2).with_text("id"),
13678            TestToken::eof("parser-test", 1, 1, 1),
13679        ]);
13680        parser.rule_context_stack = vec![RuleContextFrame {
13681            rule_index: 0,
13682            invoking_state: -1,
13683        }];
13684
13685        assert_eq!(
13686            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13687            Some(true),
13688            "at low precedence relational `>` is a single-token operator"
13689        );
13690        assert_eq!(
13691            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
13692            Some(true),
13693            "relational remains single-token at its own precedence"
13694        );
13695        assert_eq!(
13696            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13697            None,
13698            "at shift precedence, bare `>` must not force enter"
13699        );
13700    }
13701
13702    #[test]
13703    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
13704        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
13705        let mut parser = mini_parser(vec![
13706            TestToken::new(1).with_text(">"),
13707            TestToken::new(2).with_text("id"),
13708            TestToken::eof("parser-test", 1, 1, 1),
13709        ]);
13710        parser.rule_context_stack = vec![RuleContextFrame {
13711            rule_index: 0,
13712            invoking_state: -1,
13713        }];
13714
13715        assert_eq!(
13716            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13717            Some(true),
13718            "the direct relational alternative remains a one-token operator"
13719        );
13720        assert_eq!(
13721            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13722            None,
13723            "a token matched in the helper rule must return to the second shift token"
13724        );
13725    }
13726
13727    #[test]
13728    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
13729        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
13730        let mut parser = mini_parser(vec![
13731            TestToken::new(1).with_text(">"),
13732            TestToken::new(2).with_text("id"),
13733            TestToken::eof("parser-test", 1, 1, 1),
13734        ]);
13735        parser.rule_context_stack = vec![RuleContextFrame {
13736            rule_index: 0,
13737            invoking_state: -1,
13738        }];
13739
13740        assert_eq!(
13741            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13742            None,
13743            "a predicate-gated single-token path must not be hidden by a multi-token path"
13744        );
13745    }
13746
13747    #[test]
13748    fn left_recursive_loop_defers_predicate_guarded_operator() {
13749        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
13750        let mut parser = mini_parser_with_hooks(
13751            vec![
13752                TestToken::new(1).with_text("operator"),
13753                TestToken::eof("parser-test", 1, 1, 1),
13754            ],
13755            RejectingPredicateHooks::default(),
13756        );
13757        parser.rule_context_stack = vec![RuleContextFrame {
13758            rule_index: 0,
13759            invoking_state: -1,
13760        }];
13761
13762        assert_eq!(
13763            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13764            None,
13765            "a false predicate must be evaluated before entering the operator alternative"
13766        );
13767        assert_eq!(
13768            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13769            None,
13770            "cached predicate-dependent lookahead must keep deferring"
13771        );
13772    }
13773
13774    #[test]
13775    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
13776        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
13777        let mut parser = parser_inside_left_recursive_callee(1);
13778
13779        assert_eq!(
13780            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13781            None
13782        );
13783        assert_eq!(
13784            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13785            None,
13786            "the cached overlap must preserve the nullable child return path"
13787        );
13788    }
13789
13790    #[test]
13791    fn left_recursive_loop_defers_through_nullable_parent_return() {
13792        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
13793        let mut parser = mini_parser(vec![
13794            TestToken::new(1).with_text("lookahead"),
13795            TestToken::eof("parser-test", 1, 1, 1),
13796        ]);
13797        parser.rule_context_stack = vec![
13798            RuleContextFrame {
13799                rule_index: 0,
13800                invoking_state: -1,
13801            },
13802            RuleContextFrame {
13803                rule_index: 1,
13804                invoking_state: 1,
13805            },
13806            RuleContextFrame {
13807                rule_index: 2,
13808                invoking_state: 5,
13809            },
13810        ];
13811
13812        assert_eq!(
13813            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13814            None,
13815            "a nullable caller must unwind to its parent's consuming follow path"
13816        );
13817        assert_eq!(
13818            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13819            None,
13820            "the caller-overlap cache must not retain a false negative"
13821        );
13822    }
13823
13824    #[test]
13825    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
13826        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
13827        let mut parser = mini_parser(vec![
13828            TestToken::new(1).with_text("lookahead"),
13829            TestToken::eof("parser-test", 1, 1, 1),
13830        ]);
13831        parser.rule_context_stack = vec![
13832            RuleContextFrame {
13833                rule_index: 0,
13834                invoking_state: -1,
13835            },
13836            RuleContextFrame {
13837                rule_index: 1,
13838                invoking_state: 1,
13839            },
13840            RuleContextFrame {
13841                rule_index: 1,
13842                invoking_state: 8,
13843            },
13844        ];
13845
13846        assert_eq!(
13847            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13848            None,
13849            "a recursive operand return must preserve its parent caller context"
13850        );
13851        assert_eq!(
13852            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13853            None,
13854            "the caller-overlap cache must preserve the loop-boundary return"
13855        );
13856    }
13857
13858    fn token_then_eof_atn() -> Atn {
13859        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13860            4, 1, 2, // version, parser, max token type
13861            3, // states
13862            2, 0, // rule start
13863            1, 0, // basic
13864            7, 0, // rule stop
13865            0, // non-greedy states
13866            0, // precedence states
13867            1, // rules
13868            0, // rule 0 start
13869            0, // modes
13870            0, // sets
13871            2, // transitions
13872            0, 1, 5, 1, 0, 0, // match token 1
13873            1, 2, 5, -1, 0, 0, // match EOF
13874            0, // decisions
13875        ]))
13876        .deserialize_parser()
13877        .expect("artificial parser ATN should deserialize")
13878    }
13879
13880    fn epsilon_cycle_atn() -> Atn {
13881        let mut atn = ParserAtnBuilder::new(1);
13882        for (state_number, kind) in [
13883            (0, AtnStateKind::RuleStart),
13884            (1, AtnStateKind::Basic),
13885            (2, AtnStateKind::RuleStop),
13886        ] {
13887            assert_eq!(
13888                atn.add_state(kind, Some(0)).expect("state").index(),
13889                state_number
13890            );
13891        }
13892        atn.set_rule_to_start_state(vec![0])
13893            .expect("rule start states");
13894        atn.set_rule_to_stop_state(vec![2])
13895            .expect("rule stop states");
13896        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13897            .expect("transition");
13898        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
13899            .expect("self-cycle transition");
13900        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13901            .expect("exit transition");
13902        finish_atn(atn)
13903    }
13904
13905    fn eof_then_action_atn() -> Atn {
13906        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13907            4, 1, 1, // version, parser, max token type
13908            3, // states
13909            2, 0, // rule start
13910            1, 0, // basic
13911            7, 0, // rule stop
13912            0, // non-greedy states
13913            0, // precedence states
13914            1, // rules
13915            0, // rule 0 start
13916            0, // modes
13917            0, // sets
13918            2, // transitions
13919            0, 1, 5, -1, 0, 0, // match EOF
13920            1, 2, 6, 0, 0, 0, // parser action
13921            0, // decisions
13922        ]))
13923        .deserialize_parser()
13924        .expect("artificial parser ATN should deserialize")
13925    }
13926
13927    fn noop_action_then_token_then_eof_atn() -> Atn {
13928        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13929            4, 1, 2, // version, parser, max token type
13930            4, // states
13931            2, 0, // rule start
13932            1, 0, // basic
13933            1, 0, // basic
13934            7, 0, // rule stop
13935            0, // non-greedy states
13936            0, // precedence states
13937            1, // rules
13938            0, // rule 0 start
13939            0, // modes
13940            0, // sets
13941            3, // transitions
13942            0, 1, 6, 0, -1, 0, // no-op parser action
13943            1, 2, 5, 1, 0, 0, // match token 1
13944            2, 3, 5, -1, 0, 0, // match EOF
13945            0, // decisions
13946        ]))
13947        .deserialize_parser()
13948        .expect("artificial no-op action ATN should deserialize")
13949    }
13950
13951    fn two_alt_decision_atn() -> Atn {
13952        let mut atn = ParserAtnBuilder::new(2);
13953        assert_eq!(
13954            atn.add_state(AtnStateKind::RuleStart, Some(0))
13955                .expect("state")
13956                .index(),
13957            0
13958        );
13959        assert_eq!(
13960            atn.add_state(AtnStateKind::BlockStart, Some(0))
13961                .expect("state")
13962                .index(),
13963            1
13964        );
13965        assert_eq!(
13966            atn.add_state(AtnStateKind::Basic, Some(0))
13967                .expect("state")
13968                .index(),
13969            2
13970        );
13971        assert_eq!(
13972            atn.add_state(AtnStateKind::Basic, Some(0))
13973                .expect("state")
13974                .index(),
13975            3
13976        );
13977        assert_eq!(
13978            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13979                .expect("state")
13980                .index(),
13981            4
13982        );
13983        assert_eq!(
13984            atn.add_state(AtnStateKind::RuleStop, Some(0))
13985                .expect("state")
13986                .index(),
13987            5
13988        );
13989        atn.set_rule_to_start_state(vec![0])
13990            .expect("rule start states");
13991        atn.set_rule_to_stop_state(vec![5])
13992            .expect("rule stop states");
13993        atn.add_decision_state(1).expect("decision state");
13994        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13995            .expect("transition");
13996        atn.add_transition(
13997            1,
13998            ParserTransitionSpec::Atom {
13999                target: 2,
14000                label: 1,
14001            },
14002        )
14003        .expect("transition");
14004        atn.add_transition(
14005            1,
14006            ParserTransitionSpec::Atom {
14007                target: 3,
14008                label: 2,
14009            },
14010        )
14011        .expect("transition");
14012        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
14013            .expect("transition");
14014        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14015            .expect("transition");
14016        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14017            .expect("transition");
14018        finish_atn(atn)
14019    }
14020
14021    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
14022    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
14023    fn optional_then_b_eof_atn() -> Atn {
14024        let mut atn = ParserAtnBuilder::new(3);
14025        assert_eq!(
14026            atn.add_state(AtnStateKind::RuleStart, Some(0))
14027                .expect("state")
14028                .index(),
14029            0
14030        );
14031        assert_eq!(
14032            atn.add_state(AtnStateKind::BlockStart, Some(0))
14033                .expect("state")
14034                .index(),
14035            1
14036        );
14037        assert_eq!(
14038            atn.add_state(AtnStateKind::Basic, Some(0))
14039                .expect("state")
14040                .index(),
14041            2
14042        );
14043        assert_eq!(
14044            atn.add_state(AtnStateKind::Basic, Some(0))
14045                .expect("state")
14046                .index(),
14047            3
14048        );
14049        assert_eq!(
14050            atn.add_state(AtnStateKind::Basic, Some(0))
14051                .expect("state")
14052                .index(),
14053            4
14054        );
14055        assert_eq!(
14056            atn.add_state(AtnStateKind::RuleStop, Some(0))
14057                .expect("state")
14058                .index(),
14059            5
14060        );
14061        atn.set_rule_to_start_state(vec![0])
14062            .expect("rule start states");
14063        atn.set_rule_to_stop_state(vec![5])
14064            .expect("rule stop states");
14065        atn.add_decision_state(1).expect("decision state");
14066        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14067            .expect("transition");
14068        // Optional block: match A then fall through, or skip straight to state 3.
14069        atn.add_transition(
14070            1,
14071            ParserTransitionSpec::Atom {
14072                target: 3,
14073                label: 1,
14074            },
14075        )
14076        .expect("transition");
14077        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14078            .expect("transition");
14079        // Match B, then EOF.
14080        atn.add_transition(
14081            3,
14082            ParserTransitionSpec::Atom {
14083                target: 4,
14084                label: 2,
14085            },
14086        )
14087        .expect("transition");
14088        atn.add_transition(
14089            4,
14090            ParserTransitionSpec::Atom {
14091                target: 5,
14092                label: TOKEN_EOF,
14093            },
14094        )
14095        .expect("transition");
14096        finish_atn(atn)
14097    }
14098
14099    #[test]
14100    fn sync_decision_deletes_only_a_single_token() {
14101        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
14102        // expected. `(A)? B EOF` at the optional-block decision:
14103        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
14104        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
14105        //               without consuming and records the expected set for the
14106        //               subsequent mismatch (the parser must not over-consume both
14107        //               `C`s and accept the input).
14108        let atn = optional_then_b_eof_atn();
14109
14110        let mut single = mini_parser(vec![
14111            TestToken::new(3).with_text("c"),
14112            TestToken::new(2).with_text("b"),
14113            TestToken::eof("parser-test", 1, 2, 2),
14114        ]);
14115        single.rule_context_stack = vec![RuleContextFrame {
14116            rule_index: 0,
14117            invoking_state: 0,
14118        }];
14119        let children = single
14120            .sync_decision(&atn, 1, true, false)
14121            .expect("single extraneous token recovers");
14122        assert_eq!(children.len(), 1);
14123        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
14124        assert_eq!(single.number_of_syntax_errors(), 1);
14125        // Exactly one token consumed (the cursor now sits on `b`).
14126        assert_eq!(single.la(1), 2);
14127
14128        let mut double = mini_parser(vec![
14129            TestToken::new(3).with_text("c"),
14130            TestToken::new(3).with_text("c"),
14131            TestToken::new(2).with_text("b"),
14132            TestToken::eof("parser-test", 1, 3, 3),
14133        ]);
14134        double.rule_context_stack = vec![RuleContextFrame {
14135            rule_index: 0,
14136            invoking_state: 0,
14137        }];
14138        let result = double.sync_decision(&atn, 1, true, false);
14139        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
14140        // consume either `c`. It reports the mismatch at the first `c` (so the parser
14141        // does not over-consume both and accept the input). Nothing is consumed, so
14142        // the cursor still sits on the first `c` for rule-level recovery.
14143        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
14144        match error {
14145            AntlrError::ParserError { message, .. } => {
14146                assert!(message.starts_with("mismatched input"), "got: {message}");
14147            }
14148            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
14149        }
14150        assert_eq!(double.la(1), 3);
14151    }
14152
14153    /// The real serialized ATN that `antlr4-rust-gen` emits for
14154    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
14155    /// the loop is `EOF`. The loop decision is state 5.
14156    fn star_loop_then_eof_atn() -> Atn {
14157        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14158            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,
14159            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,
14160            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,
14161            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
14162        ]))
14163        .deserialize_parser()
14164        .expect("star-loop-then-EOF ATN should deserialize")
14165    }
14166
14167    /// ATN for `s : a+ Y ; a : X ;`.
14168    ///
14169    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
14170    /// `+` loop must not treat that zero-width child as a successful iteration
14171    /// and then re-enter the loop at the same token index.
14172    fn plus_loop_with_recovering_body_atn() -> Atn {
14173        let mut atn = ParserAtnBuilder::new(2);
14174        assert_eq!(
14175            atn.add_state(AtnStateKind::RuleStart, Some(0))
14176                .expect("state")
14177                .index(),
14178            0
14179        );
14180        assert_eq!(
14181            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
14182                .expect("state")
14183                .index(),
14184            1
14185        );
14186        assert_eq!(
14187            atn.add_state(AtnStateKind::Basic, Some(0))
14188                .expect("state")
14189                .index(),
14190            2
14191        );
14192        assert_eq!(
14193            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14194                .expect("state")
14195                .index(),
14196            3
14197        );
14198        assert_eq!(
14199            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
14200                .expect("state")
14201                .index(),
14202            4
14203        );
14204        assert_eq!(
14205            atn.add_state(AtnStateKind::LoopEnd, Some(0))
14206                .expect("state")
14207                .index(),
14208            5
14209        );
14210        assert_eq!(
14211            atn.add_state(AtnStateKind::RuleStop, Some(0))
14212                .expect("state")
14213                .index(),
14214            6
14215        );
14216        assert_eq!(
14217            atn.add_state(AtnStateKind::RuleStart, Some(1))
14218                .expect("state")
14219                .index(),
14220            7
14221        );
14222        assert_eq!(
14223            atn.add_state(AtnStateKind::Basic, Some(1))
14224                .expect("state")
14225                .index(),
14226            8
14227        );
14228        assert_eq!(
14229            atn.add_state(AtnStateKind::RuleStop, Some(1))
14230                .expect("state")
14231                .index(),
14232            9
14233        );
14234        atn.set_rule_to_start_state(vec![0, 7])
14235            .expect("rule start states");
14236        atn.set_rule_to_stop_state(vec![6, 9])
14237            .expect("rule stop states");
14238        atn.set_end_state(1, 3).expect("block end state");
14239        atn.set_loop_back_state(5, 4).expect("loop back state");
14240        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14241            .expect("transition");
14242        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14243            .expect("transition");
14244        atn.add_transition(
14245            2,
14246            ParserTransitionSpec::Rule {
14247                target: 7,
14248                rule_index: 1,
14249                follow_state: 3,
14250                precedence: 0,
14251            },
14252        )
14253        .expect("transition");
14254        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14255            .expect("transition");
14256        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
14257            .expect("transition");
14258        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14259            .expect("transition");
14260        atn.add_transition(
14261            5,
14262            ParserTransitionSpec::Atom {
14263                target: 6,
14264                label: 2,
14265            },
14266        )
14267        .expect("transition");
14268        atn.add_transition(
14269            7,
14270            ParserTransitionSpec::Atom {
14271                target: 8,
14272                label: 1,
14273            },
14274        )
14275        .expect("transition");
14276        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14277            .expect("transition");
14278        finish_atn(atn)
14279    }
14280
14281    #[test]
14282    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
14283        let atn = plus_loop_with_recovering_body_atn();
14284        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14285
14286        let error = parser
14287            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
14288            .expect_err("EOF recovery should report a bounded mismatch");
14289
14290        let AntlrError::ParserError { message, .. } = error else {
14291            panic!("expected ParserError, got {error:?}");
14292        };
14293        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
14294        assert_eq!(parser.number_of_syntax_errors(), 1);
14295        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
14296    }
14297
14298    #[test]
14299    fn sync_decision_deletes_token_before_eof_at_loop_back() {
14300        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
14301        // At the loop ENTRY (loop_back = false) a single unexpected token before
14302        // EOF is deleted as an error node (then the generated EOF match consumes
14303        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
14304        // EOF must be a valid scan-stop for this to fire.
14305        let atn = star_loop_then_eof_atn();
14306        let mut parser = mini_parser(vec![
14307            TestToken::new(2).with_text("c"),
14308            TestToken::eof("parser-test", 1, 1, 1),
14309        ]);
14310        parser.rule_context_stack = vec![RuleContextFrame {
14311            rule_index: 0,
14312            invoking_state: 0,
14313        }];
14314        let children = parser
14315            .sync_decision(&atn, 5, true, false)
14316            .expect("single token before EOF recovers");
14317        assert_eq!(children.len(), 1);
14318        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
14319        assert_eq!(parser.number_of_syntax_errors(), 1);
14320        assert_eq!(
14321            parser.la(1),
14322            TOKEN_EOF,
14323            "EOF is left for the rule's EOF match"
14324        );
14325    }
14326
14327    #[test]
14328    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
14329        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
14330        // single-token deletion, which fails because LA(2) = `c` is not expected —
14331        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
14332        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
14333        let atn = star_loop_then_eof_atn();
14334        let mut parser = mini_parser(vec![
14335            TestToken::new(2).with_text("c"),
14336            TestToken::new(2).with_text("c"),
14337            TestToken::eof("parser-test", 1, 2, 2),
14338        ]);
14339        parser.rule_context_stack = vec![RuleContextFrame {
14340            rule_index: 0,
14341            invoking_state: 0,
14342        }];
14343        let error = parser
14344            .sync_decision(&atn, 5, true, false)
14345            .expect_err("two tokens at the loop entry must not be deleted");
14346        match error {
14347            AntlrError::ParserError { message, .. } => {
14348                assert!(message.starts_with("mismatched input"), "got: {message}");
14349            }
14350            other => panic!("expected mismatched-input ParserError, got {other:?}"),
14351        }
14352        assert_eq!(
14353            parser.la(1),
14354            2,
14355            "nothing consumed; cursor still on first `c`"
14356        );
14357    }
14358
14359    #[test]
14360    fn sync_decision_consumes_until_eof_at_loop_back() {
14361        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
14362        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
14363        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
14364        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
14365        // post-`a` state directly: `c c <EOF>` with loop_back = true.
14366        let atn = star_loop_then_eof_atn();
14367        let mut parser = mini_parser(vec![
14368            TestToken::new(2).with_text("c"),
14369            TestToken::new(2).with_text("c"),
14370            TestToken::eof("parser-test", 1, 2, 2),
14371        ]);
14372        parser.rule_context_stack = vec![RuleContextFrame {
14373            rule_index: 0,
14374            invoking_state: 0,
14375        }];
14376        let children = parser
14377            .sync_decision(&atn, 5, false, true)
14378            .expect("loop-back multi-token deletion recovers onto EOF");
14379        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
14380        assert!(
14381            children
14382                .iter()
14383                .all(|child| parser.node(*child).kind() == NodeKind::Error)
14384        );
14385        assert_eq!(parser.number_of_syntax_errors(), 1);
14386        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
14387    }
14388
14389    fn predicate_after_token_atn() -> Atn {
14390        let mut atn = ParserAtnBuilder::new(2);
14391        assert_eq!(
14392            atn.add_state(AtnStateKind::RuleStart, Some(0))
14393                .expect("state")
14394                .index(),
14395            0
14396        );
14397        assert_eq!(
14398            atn.add_state(AtnStateKind::Basic, Some(0))
14399                .expect("state")
14400                .index(),
14401            1
14402        );
14403        assert_eq!(
14404            atn.add_state(AtnStateKind::Basic, Some(0))
14405                .expect("state")
14406                .index(),
14407            2
14408        );
14409        assert_eq!(
14410            atn.add_state(AtnStateKind::Basic, Some(0))
14411                .expect("state")
14412                .index(),
14413            3
14414        );
14415        assert_eq!(
14416            atn.add_state(AtnStateKind::RuleStop, Some(0))
14417                .expect("state")
14418                .index(),
14419            4
14420        );
14421        atn.set_rule_to_start_state(vec![0])
14422            .expect("rule start states");
14423        atn.set_rule_to_stop_state(vec![4])
14424            .expect("rule stop states");
14425        atn.add_transition(
14426            0,
14427            ParserTransitionSpec::Atom {
14428                target: 1,
14429                label: 1,
14430            },
14431        )
14432        .expect("transition");
14433        atn.add_transition(
14434            1,
14435            ParserTransitionSpec::Predicate {
14436                target: 2,
14437                rule_index: 0,
14438                pred_index: 0,
14439                context_dependent: false,
14440            },
14441        )
14442        .expect("transition");
14443        atn.add_transition(
14444            2,
14445            ParserTransitionSpec::Atom {
14446                target: 3,
14447                label: 2,
14448            },
14449        )
14450        .expect("transition");
14451        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14452            .expect("transition");
14453        finish_atn(atn)
14454    }
14455
14456    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
14457        let mut atn = ParserAtnBuilder::new(1);
14458        for (state_number, kind) in [
14459            (0, AtnStateKind::RuleStart),
14460            (1, AtnStateKind::BlockStart),
14461            (2, AtnStateKind::Basic),
14462            (3, AtnStateKind::Basic),
14463            (4, AtnStateKind::Basic),
14464            (5, AtnStateKind::Basic),
14465            (6, AtnStateKind::BlockEnd),
14466            (7, AtnStateKind::RuleStop),
14467        ] {
14468            assert_eq!(
14469                atn.add_state(kind, Some(0)).expect("state").index(),
14470                state_number
14471            );
14472        }
14473        atn.set_rule_to_start_state(vec![0])
14474            .expect("rule start states");
14475        atn.set_rule_to_stop_state(vec![7])
14476            .expect("rule stop states");
14477        atn.add_decision_state(1).expect("decision state");
14478        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14479            .expect("transition");
14480        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14481            .expect("transition");
14482        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14483            .expect("transition");
14484        atn.add_transition(
14485            2,
14486            ParserTransitionSpec::Predicate {
14487                target: 4,
14488                rule_index: 0,
14489                pred_index: pred_indexes[0],
14490                context_dependent: false,
14491            },
14492        )
14493        .expect("transition");
14494        atn.add_transition(
14495            3,
14496            ParserTransitionSpec::Predicate {
14497                target: 5,
14498                rule_index: 0,
14499                pred_index: pred_indexes[1],
14500                context_dependent: false,
14501            },
14502        )
14503        .expect("transition");
14504        atn.add_transition(
14505            4,
14506            ParserTransitionSpec::Atom {
14507                target: 6,
14508                label: 1,
14509            },
14510        )
14511        .expect("transition");
14512        atn.add_transition(
14513            5,
14514            ParserTransitionSpec::Atom {
14515                target: 6,
14516                label: 1,
14517            },
14518        )
14519        .expect("transition");
14520        atn.add_transition(
14521            6,
14522            ParserTransitionSpec::Atom {
14523                target: 7,
14524                label: TOKEN_EOF,
14525            },
14526        )
14527        .expect("transition");
14528        finish_atn(atn)
14529    }
14530
14531    fn nested_nullable_context_atn() -> Atn {
14532        let mut atn = ParserAtnBuilder::new(1);
14533        for state_number in 0..=20 {
14534            let kind = match state_number {
14535                0 | 10 | 16 => AtnStateKind::RuleStart,
14536                9 | 15 | 20 => AtnStateKind::RuleStop,
14537                _ => AtnStateKind::Basic,
14538            };
14539            let rule_index = match state_number {
14540                0..=9 => 0,
14541                10..=15 => 1,
14542                _ => 2,
14543            };
14544            assert_eq!(
14545                atn.add_state(kind, Some(rule_index))
14546                    .expect("state")
14547                    .index(),
14548                state_number
14549            );
14550        }
14551        atn.set_rule_to_start_state(vec![0, 10, 16])
14552            .expect("rule start states");
14553        atn.set_rule_to_stop_state(vec![9, 15, 20])
14554            .expect("rule stop states");
14555        atn.add_transition(
14556            1,
14557            ParserTransitionSpec::Rule {
14558                target: 10,
14559                rule_index: 1,
14560                follow_state: 8,
14561                precedence: 0,
14562            },
14563        )
14564        .expect("transition");
14565        atn.add_transition(
14566            8,
14567            ParserTransitionSpec::Atom {
14568                target: 9,
14569                label: 1,
14570            },
14571        )
14572        .expect("transition");
14573        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14574            .expect("transition");
14575        atn.add_transition(
14576            2,
14577            ParserTransitionSpec::Rule {
14578                target: 16,
14579                rule_index: 2,
14580                follow_state: 14,
14581                precedence: 0,
14582            },
14583        )
14584        .expect("transition");
14585        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
14586            .expect("transition");
14587        finish_atn(atn)
14588    }
14589
14590    fn generated_match_recovery_atn() -> Atn {
14591        let mut atn = ParserAtnBuilder::new(2);
14592        assert_eq!(
14593            atn.add_state(AtnStateKind::RuleStart, Some(0))
14594                .expect("state")
14595                .index(),
14596            0
14597        );
14598        assert_eq!(
14599            atn.add_state(AtnStateKind::Basic, Some(0))
14600                .expect("state")
14601                .index(),
14602            1
14603        );
14604        assert_eq!(
14605            atn.add_state(AtnStateKind::Basic, Some(0))
14606                .expect("state")
14607                .index(),
14608            2
14609        );
14610        assert_eq!(
14611            atn.add_state(AtnStateKind::RuleStop, Some(0))
14612                .expect("state")
14613                .index(),
14614            3
14615        );
14616        assert_eq!(
14617            atn.add_state(AtnStateKind::RuleStart, Some(1))
14618                .expect("state")
14619                .index(),
14620            4
14621        );
14622        assert_eq!(
14623            atn.add_state(AtnStateKind::RuleStop, Some(1))
14624                .expect("state")
14625                .index(),
14626            5
14627        );
14628        atn.set_rule_to_start_state(vec![0, 4])
14629            .expect("rule start states");
14630        atn.set_rule_to_stop_state(vec![3, 5])
14631            .expect("rule stop states");
14632        atn.add_transition(
14633            1,
14634            ParserTransitionSpec::Rule {
14635                target: 4,
14636                rule_index: 1,
14637                follow_state: 2,
14638                precedence: 0,
14639            },
14640        )
14641        .expect("transition");
14642        atn.add_transition(
14643            2,
14644            ParserTransitionSpec::Atom {
14645                target: 3,
14646                label: TOKEN_EOF,
14647            },
14648        )
14649        .expect("transition");
14650        finish_atn(atn)
14651    }
14652
14653    fn complement_set_atn() -> Atn {
14654        let mut atn = ParserAtnBuilder::new(1);
14655        assert_eq!(
14656            atn.add_state(AtnStateKind::RuleStart, Some(0))
14657                .expect("state")
14658                .index(),
14659            0
14660        );
14661        assert_eq!(
14662            atn.add_state(AtnStateKind::RuleStop, Some(0))
14663                .expect("state")
14664                .index(),
14665            1
14666        );
14667        atn.set_rule_to_start_state(vec![0])
14668            .expect("rule start states");
14669        atn.set_rule_to_stop_state(vec![1])
14670            .expect("rule stop states");
14671        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
14672        atn.add_transition(
14673            0,
14674            ParserTransitionSpec::NotSet {
14675                target: 1,
14676                set: excluded,
14677            },
14678        )
14679        .expect("transition");
14680        finish_atn(atn)
14681    }
14682
14683    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
14684    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
14685    fn wildcard_then_eof_atn() -> Atn {
14686        let mut atn = ParserAtnBuilder::new(1);
14687        assert_eq!(
14688            atn.add_state(AtnStateKind::RuleStart, Some(0))
14689                .expect("state")
14690                .index(),
14691            0
14692        );
14693        assert_eq!(
14694            atn.add_state(AtnStateKind::RuleStop, Some(0))
14695                .expect("state")
14696                .index(),
14697            1
14698        );
14699        assert_eq!(
14700            atn.add_state(AtnStateKind::Basic, Some(0))
14701                .expect("state")
14702                .index(),
14703            2
14704        );
14705        atn.set_rule_to_start_state(vec![0])
14706            .expect("rule start states");
14707        atn.set_rule_to_stop_state(vec![1])
14708            .expect("rule stop states");
14709        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
14710            .expect("transition");
14711        atn.add_transition(
14712            2,
14713            ParserTransitionSpec::Atom {
14714                target: 1,
14715                label: TOKEN_EOF,
14716            },
14717        )
14718        .expect("transition");
14719        finish_atn(atn)
14720    }
14721
14722    #[test]
14723    fn parser_matches_token_and_reports_mismatch() {
14724        let source = Source {
14725            tokens: vec![
14726                TestToken::new(1).with_text("x"),
14727                TestToken::eof("parser-test", 1, 1, 1),
14728            ],
14729            index: 0,
14730        };
14731        let data = RecognizerData::new(
14732            "Mini.g4",
14733            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14734        );
14735        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
14736        let matched = parser.match_token(1).expect("token 1 should match");
14737        assert_eq!(parser.node(matched).text(), "x");
14738        assert!(parser.match_token(1).is_err());
14739    }
14740
14741    #[test]
14742    fn parser_matches_token_sets() {
14743        let mut parser = mini_parser(vec![
14744            TestToken::new(1).with_text("x"),
14745            TestToken::eof("parser-test", 1, 1, 1),
14746        ]);
14747
14748        let matched = parser
14749            .match_set(&[(1, 1), (3, 4)])
14750            .expect("token set should match");
14751        assert_eq!(parser.node(matched).text(), "x");
14752        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
14753    }
14754
14755    #[test]
14756    fn generated_rule_api_tracks_state_and_precedence() {
14757        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14758
14759        let context = parser.enter_rule(7, 2);
14760        assert_eq!(context.rule_index(), 2);
14761        assert_eq!(parser.state(), 7);
14762        assert_eq!(
14763            parser.rule_context_stack,
14764            vec![RuleContextFrame {
14765                rule_index: 2,
14766                invoking_state: 7
14767            }]
14768        );
14769
14770        let recursive = parser.enter_recursion_rule(11, 3, 4);
14771        assert_eq!(recursive.rule_index(), 3);
14772        assert!(parser.precpred(4));
14773        assert!(parser.precpred(5));
14774        assert!(!parser.precpred(3));
14775
14776        let next = parser.push_new_recursion_context(13, 3);
14777        assert_eq!(next.invoking_state(), 13);
14778        parser.unroll_recursion_context();
14779        assert_eq!(parser.precedence_stack, vec![0]);
14780        assert_eq!(
14781            parser.rule_context_stack,
14782            vec![RuleContextFrame {
14783                rule_index: 2,
14784                invoking_state: 7
14785            }]
14786        );
14787
14788        parser.exit_rule();
14789        assert!(parser.rule_context_stack.is_empty());
14790    }
14791
14792    #[test]
14793    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
14794        let mut parser = mini_parser(vec![
14795            TestToken::new(1).with_text("x"),
14796            TestToken::eof("parser-test", 1, 1, 1),
14797        ]);
14798        let matched = parser.match_token(1).expect("token should match");
14799        assert_eq!(parser.node(matched).text(), "x");
14800        parser.record_generated_syntax_error();
14801        parser.set_int_member(7, 11);
14802        parser.set_build_parse_trees(false);
14803        parser.set_report_diagnostic_errors(true);
14804        parser.set_prediction_mode(PredictionMode::Sll);
14805        parser.set_bail_on_error(true);
14806        let _context = parser.enter_recursion_rule(9, 0, 4);
14807        parser.pending_invoking_states.push(5);
14808        parser.unknown_predicate_hits.push((0, 1));
14809        parser.unhandled_action_hits.push((0, 2));
14810
14811        parser.reset();
14812
14813        assert_eq!(parser.input.index(), 0);
14814        assert_eq!(parser.la(1), 1);
14815        assert_eq!(parser.state(), -1);
14816        assert_eq!(parser.number_of_syntax_errors(), 0);
14817        assert_eq!(parser.parse_tree_storage().node_count(), 0);
14818        assert!(parser.rule_context_stack.is_empty());
14819        assert!(parser.pending_invoking_states.is_empty());
14820        assert_eq!(parser.precedence_stack, [0]);
14821        assert!(parser.unknown_predicate_hits.is_empty());
14822        assert!(parser.unhandled_action_hits.is_empty());
14823        assert_eq!(parser.int_member(7), Some(11));
14824        assert!(!parser.build_parse_trees());
14825        assert!(parser.report_diagnostic_errors());
14826        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
14827        assert!(parser.bail_on_error());
14828    }
14829
14830    #[test]
14831    fn set_token_stream_replaces_input_and_resets_parser() {
14832        let mut parser = mini_parser(vec![
14833            TestToken::new(1).with_text("old"),
14834            TestToken::eof("parser-test", 1, 1, 1),
14835        ]);
14836        parser.consume();
14837        parser.record_generated_syntax_error();
14838        let replacement = CommonTokenStream::new(Source {
14839            tokens: vec![
14840                TestToken::new(2).with_text("new"),
14841                TestToken::eof("parser-test", 1, 1, 1),
14842            ],
14843            index: 0,
14844        });
14845
14846        parser.set_token_stream(replacement);
14847
14848        assert_eq!(parser.input.index(), 0);
14849        assert_eq!(parser.la(1), 2);
14850        assert_eq!(parser.input.text_all(), "new");
14851        assert_eq!(parser.number_of_syntax_errors(), 0);
14852    }
14853
14854    #[test]
14855    fn active_invocation_states_exclude_the_root_frame() {
14856        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14857
14858        let _root = parser.enter_rule(0, 0);
14859        assert!(parser.active_invocation_states().is_empty());
14860
14861        let marker = parser.push_invoking_state(6);
14862        let _child = parser.enter_rule(2, 1);
14863        parser.discard_invoking_state(marker);
14864        assert_eq!(parser.active_invocation_states(), [6]);
14865
14866        let marker = parser.push_invoking_state(13);
14867        let _grandchild = parser.enter_rule(4, 2);
14868        parser.discard_invoking_state(marker);
14869        assert_eq!(parser.active_invocation_states(), [13, 6]);
14870
14871        parser.exit_rule();
14872        parser.exit_rule();
14873        parser.exit_rule();
14874    }
14875
14876    #[test]
14877    fn parser_predicates_support_token_adjacency() {
14878        let mut parser = mini_parser(vec![
14879            TestToken::new(1).with_text("=").with_span(0, 0),
14880            TestToken::new(1).with_text(">").with_span(1, 1),
14881            TestToken::eof("parser-test", 2, 1, 2),
14882        ]);
14883        parser.consume();
14884        parser.consume();
14885
14886        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
14887
14888        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14889
14890        let mut parser = mini_parser(vec![
14891            TestToken::new(1).with_text("=").with_span(0, 0),
14892            TestToken::new(1)
14893                .with_text(" ")
14894                .with_channel(HIDDEN_CHANNEL)
14895                .with_span(1, 1),
14896            TestToken::new(1).with_text(">").with_span(2, 2),
14897            TestToken::eof("parser-test", 3, 1, 3),
14898        ]);
14899        parser.consume();
14900        parser.consume();
14901
14902        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14903    }
14904
14905    #[test]
14906    fn parser_predicates_support_context_child_text_checks() {
14907        let mut parser = mini_parser(vec![
14908            TestToken::new(1).with_text("var"),
14909            TestToken::eof("parser-test", 1, 1, 1),
14910        ]);
14911        let mut context = ParserRuleContext::new(1, 0);
14912        let mut child_context = ParserRuleContext::new(2, 0);
14913        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
14914        parser.tree.add_child(&mut child_context, terminal);
14915        let child = parser.rule_node(child_context);
14916        parser.tree.add_child(&mut context, child);
14917        let predicates = [(
14918            1,
14919            0,
14920            ParserPredicate::ContextChildRuleTextNotEquals {
14921                rule_index: 2,
14922                text: "var",
14923            },
14924        )];
14925
14926        assert!(
14927            !parser.parser_semantic_predicate_matches_with_context_and_local(
14928                &predicates,
14929                1,
14930                0,
14931                &context,
14932                0,
14933            )
14934        );
14935    }
14936
14937    #[test]
14938    fn context_expected_symbols_walks_nullable_parent_contexts() {
14939        let atn = nested_nullable_context_atn();
14940        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14941        parser.rule_context_stack = vec![
14942            RuleContextFrame {
14943                rule_index: 0,
14944                invoking_state: 0,
14945            },
14946            RuleContextFrame {
14947                rule_index: 1,
14948                invoking_state: 1,
14949            },
14950            RuleContextFrame {
14951                rule_index: 2,
14952                invoking_state: 2,
14953            },
14954        ];
14955
14956        let expected = parser.context_expected_symbols(&atn);
14957
14958        assert!(expected.contains(&1));
14959        assert!(expected.contains(&TOKEN_EOF));
14960    }
14961
14962    #[test]
14963    fn prediction_context_return_states_track_rule_stack_changes() {
14964        let atn = nested_nullable_context_atn();
14965        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14966        parser.rule_context_stack = vec![
14967            RuleContextFrame {
14968                rule_index: 0,
14969                invoking_state: 0,
14970            },
14971            RuleContextFrame {
14972                rule_index: 1,
14973                invoking_state: 1,
14974            },
14975            RuleContextFrame {
14976                rule_index: 2,
14977                invoking_state: 2,
14978            },
14979        ];
14980
14981        let initial_version = parser.rule_context_version();
14982        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14983        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14984        assert_eq!(first, second);
14985        assert_eq!(parser.rule_context_version(), initial_version);
14986
14987        parser.exit_rule();
14988        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14989        assert_ne!(first, after_pop);
14990        assert_ne!(parser.rule_context_version(), initial_version);
14991    }
14992
14993    #[test]
14994    fn generated_match_token_recovers_missing_token_from_context_follow() {
14995        let atn = generated_match_recovery_atn();
14996        let data = RecognizerData::new(
14997            "Mini.g4",
14998            Vocabulary::new(
14999                [None, Some("'X'"), Some("'Y'")],
15000                [None, Some("X"), Some("Y")],
15001                [None::<&str>, None, None],
15002            ),
15003        );
15004        let mut parser = BaseParser::new(
15005            CommonTokenStream::new(Source {
15006                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15007                index: 0,
15008            }),
15009            data,
15010        );
15011        parser.rule_context_stack = vec![
15012            RuleContextFrame {
15013                rule_index: 0,
15014                invoking_state: 0,
15015            },
15016            RuleContextFrame {
15017                rule_index: 1,
15018                invoking_state: 1,
15019            },
15020        ];
15021        assert_eq!(parser.number_of_syntax_errors(), 0);
15022
15023        let node = parser
15024            .match_token_recovering(2, 5, &atn)
15025            .expect("generated match should insert missing token");
15026
15027        assert_eq!(node.children().len(), 1);
15028        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
15029        assert_eq!(
15030            node.clone()
15031                .into_child_iter()
15032                .map(|child| parser.node(child).text())
15033                .collect::<Vec<_>>(),
15034            ["<missing 'Y'>"]
15035        );
15036        // Single-token insertion synthesizes a missing token and consumes nothing,
15037        // so no EOF terminal is consumed even though lookahead is EOF.
15038        assert!(!node.consumed_eof());
15039        assert_eq!(parser.la(1), TOKEN_EOF);
15040        assert_eq!(parser.number_of_syntax_errors(), 1);
15041        assert_eq!(
15042            parser.generated_parser_diagnostics,
15043            [ParserDiagnostic {
15044                line: 1,
15045                column: 3,
15046                message: "missing 'Y' at '<EOF>'".to_owned(),
15047            }]
15048        );
15049    }
15050
15051    #[test]
15052    fn generated_match_token_counts_single_token_deletion_recovery() {
15053        let atn = generated_match_recovery_atn();
15054        let data = RecognizerData::new(
15055            "Mini.g4",
15056            Vocabulary::new(
15057                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15058                [None, Some("X"), Some("Y"), Some("Z")],
15059                [None::<&str>, None, None, None],
15060            ),
15061        );
15062        let mut parser = BaseParser::new(
15063            CommonTokenStream::new(Source {
15064                tokens: vec![
15065                    TestToken::new(3).with_text("z"),
15066                    TestToken::new(2).with_text("y"),
15067                    TestToken::eof("parser-test", 3, 1, 3),
15068                ],
15069                index: 0,
15070            }),
15071            data,
15072        );
15073
15074        let node = parser
15075            .match_token_recovering(2, 5, &atn)
15076            .expect("generated match should delete the extraneous token");
15077
15078        assert_eq!(node.children().len(), 2);
15079        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
15080        assert_eq!(parser.node(node.children()[0]).text(), "z");
15081        assert_eq!(parser.node(node.children()[1]).text(), "y");
15082        assert_eq!(
15083            node.into_child_iter()
15084                .map(|child| parser.node(child).text())
15085                .collect::<Vec<_>>(),
15086            ["z", "y"]
15087        );
15088        assert_eq!(parser.number_of_syntax_errors(), 1);
15089    }
15090
15091    #[test]
15092    fn generated_match_token_iterates_single_success_without_a_children_vec() {
15093        let atn = generated_match_recovery_atn();
15094        let data = RecognizerData::new(
15095            "Mini.g4",
15096            Vocabulary::new(
15097                [None, Some("'X'"), Some("'Y'")],
15098                [None, Some("X"), Some("Y")],
15099                [None::<&str>, None, None],
15100            ),
15101        );
15102        let mut parser = BaseParser::new(
15103            CommonTokenStream::new(Source {
15104                tokens: vec![
15105                    TestToken::new(2).with_text("y"),
15106                    TestToken::eof("parser-test", 1, 1, 1),
15107                ],
15108                index: 0,
15109            }),
15110            data,
15111        );
15112
15113        let node = parser
15114            .match_token_recovering(2, 5, &atn)
15115            .expect("generated match should consume the expected token");
15116
15117        assert_eq!(
15118            node.into_child_iter()
15119                .map(|child| parser.node(child).text())
15120                .collect::<Vec<_>>(),
15121            ["y"]
15122        );
15123        assert_eq!(parser.number_of_syntax_errors(), 0);
15124    }
15125
15126    #[test]
15127    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
15128        let atn = generated_match_recovery_atn();
15129        let data = RecognizerData::new(
15130            "Mini.g4",
15131            Vocabulary::new(
15132                [None, Some("'X'"), Some("'Y'")],
15133                [None, Some("X"), Some("Y")],
15134                [None::<&str>, None, None],
15135            ),
15136        );
15137        let mut parser = BaseParser::new(
15138            CommonTokenStream::new(Source {
15139                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15140                index: 0,
15141            }),
15142            data,
15143        );
15144        parser.rule_context_stack = vec![
15145            RuleContextFrame {
15146                rule_index: 0,
15147                invoking_state: 0,
15148            },
15149            RuleContextFrame {
15150                rule_index: 1,
15151                invoking_state: 1,
15152            },
15153        ];
15154        let marker = parser.generated_diagnostics_checkpoint();
15155
15156        let _ = parser
15157            .match_token_recovering(2, 5, &atn)
15158            .expect("generated match should insert missing token");
15159        assert_eq!(parser.number_of_syntax_errors(), 1);
15160
15161        parser.restore_generated_diagnostics(marker);
15162
15163        assert_eq!(parser.number_of_syntax_errors(), 0);
15164        assert!(parser.generated_parser_diagnostics.is_empty());
15165    }
15166
15167    #[test]
15168    fn generated_prediction_diagnostics_use_adaptive_context() {
15169        let atn = two_alt_decision_atn();
15170        let data = RecognizerData::new(
15171            "Mini.g4",
15172            Vocabulary::new(
15173                [None, Some("'x'"), Some("'y'")],
15174                [None, Some("X"), Some("Y")],
15175                [None::<&str>, None, None],
15176            ),
15177        )
15178        .with_rule_names(["s"]);
15179        let mut parser = BaseParser::new(
15180            CommonTokenStream::new(Source {
15181                tokens: vec![
15182                    TestToken::new(1)
15183                        .with_text("x")
15184                        .with_position(1, 0)
15185                        .with_span(0, 0),
15186                    TestToken::new(2)
15187                        .with_text("y")
15188                        .with_position(1, 2)
15189                        .with_span(1, 1),
15190                    TestToken::eof("parser-test", 2, 1, 3),
15191                ],
15192                index: 0,
15193            }),
15194            data,
15195        );
15196        parser.set_report_diagnostic_errors(true);
15197
15198        parser.record_generated_prediction_diagnostic(
15199            &atn,
15200            1,
15201            &ParserAtnPrediction {
15202                alt: 1,
15203                requires_full_context: true,
15204                has_semantic_context: false,
15205                diagnostic: Some(ParserAtnPredictionDiagnostic {
15206                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
15207                    start_index: 0,
15208                    sll_stop_index: 1,
15209                    ll_stop_index: 0,
15210                    conflicting_alts: vec![1, 2],
15211                    exact: false,
15212                }),
15213            },
15214        );
15215        // Ambiguities from the default LL prediction mode are non-exact, so —
15216        // matching Java's exactOnly DiagnosticErrorListener — only the
15217        // attempting-full-context line is reported. Exact-ambiguity mode
15218        // reports the ambiguity itself.
15219        parser.record_generated_prediction_diagnostic(
15220            &atn,
15221            1,
15222            &ParserAtnPrediction {
15223                alt: 1,
15224                requires_full_context: true,
15225                has_semantic_context: false,
15226                diagnostic: Some(ParserAtnPredictionDiagnostic {
15227                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
15228                    start_index: 0,
15229                    sll_stop_index: 1,
15230                    ll_stop_index: 1,
15231                    conflicting_alts: vec![1, 2],
15232                    exact: false,
15233                }),
15234            },
15235        );
15236
15237        assert_eq!(
15238            parser.generated_parser_diagnostics,
15239            [
15240                ParserDiagnostic {
15241                    line: 1,
15242                    column: 2,
15243                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
15244                },
15245                ParserDiagnostic {
15246                    line: 1,
15247                    column: 0,
15248                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
15249                },
15250                ParserDiagnostic {
15251                    line: 1,
15252                    column: 2,
15253                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
15254                },
15255            ]
15256        );
15257    }
15258
15259    #[test]
15260    fn generated_match_not_set_recovers_empty_complement_at_eof() {
15261        let atn = complement_set_atn();
15262        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15263        parser.rule_context_stack = vec![RuleContextFrame {
15264            rule_index: 0,
15265            invoking_state: 0,
15266        }];
15267
15268        let node = parser
15269            .match_not_token_set_recovering(
15270                atn.token_set(0).expect("excluded token set"),
15271                1,
15272                1,
15273                1,
15274                &atn,
15275            )
15276            .expect("empty complement should recover at EOF");
15277
15278        assert_eq!(node.children().len(), 1);
15279        // Recovery synthesizes a missing token without consuming EOF, so the
15280        // enclosing rule must not record EOF as its stop token.
15281        assert!(!node.consumed_eof());
15282        assert_eq!(parser.la(1), TOKEN_EOF);
15283        assert_eq!(
15284            parser.generated_parser_diagnostics,
15285            [ParserDiagnostic {
15286                line: 1,
15287                column: 1,
15288                message: "missing {} at '<EOF>'".to_owned(),
15289            }]
15290        );
15291    }
15292
15293    #[test]
15294    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
15295        // `start : . EOF ;` on empty input. The wildcard is modeled as an
15296        // empty-complement not-set; at EOF the follow state (the explicit EOF
15297        // match) expects EOF, so even in the start rule recovery must perform
15298        // single-token insertion (`<missing ...>`) rather than aborting — matching
15299        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
15300        let atn = wildcard_then_eof_atn();
15301        let data = RecognizerData::new(
15302            "Mini.g4",
15303            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15304        );
15305        let mut parser = BaseParser::new(
15306            CommonTokenStream::new(Source {
15307                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
15308                index: 0,
15309            }),
15310            data,
15311        );
15312        parser.rule_context_stack = vec![RuleContextFrame {
15313            rule_index: 0,
15314            invoking_state: 0,
15315        }];
15316
15317        let node = parser
15318            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
15319            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
15320
15321        // A single `<missing ...>` error node is inserted; EOF is not consumed.
15322        assert_eq!(node.children().len(), 1);
15323        assert!(!node.consumed_eof());
15324        assert!(
15325            parser
15326                .node(node.children()[0])
15327                .text()
15328                .starts_with("<missing")
15329        );
15330        assert_eq!(parser.la(1), TOKEN_EOF);
15331        assert_eq!(
15332            parser.generated_parser_diagnostics,
15333            [ParserDiagnostic {
15334                line: 1,
15335                column: 1,
15336                message: "missing 'x' at '<EOF>'".to_owned(),
15337            }]
15338        );
15339    }
15340
15341    #[test]
15342    fn generated_rule_recovery_consumes_to_parent_follow() {
15343        let atn = generated_match_recovery_atn();
15344        let data = RecognizerData::new(
15345            "Mini.g4",
15346            Vocabulary::new(
15347                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15348                [None, Some("X"), Some("Y"), Some("Z")],
15349                [None::<&str>, None, None, None],
15350            ),
15351        );
15352        let mut parser = BaseParser::new(
15353            CommonTokenStream::new(Source {
15354                tokens: vec![
15355                    TestToken::new(3).with_text("z"),
15356                    TestToken::eof("parser-test", 1, 1, 1),
15357                ],
15358                index: 0,
15359            }),
15360            data,
15361        );
15362        let _parent = parser.enter_rule(0, 0);
15363        let marker = parser.push_invoking_state(1);
15364        let mut child = parser.enter_rule(4, 1);
15365        parser.discard_invoking_state(marker);
15366
15367        parser.recover_generated_rule(
15368            &mut child,
15369            &atn,
15370            AntlrError::ParserError {
15371                line: 1,
15372                column: 0,
15373                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15374            },
15375        );
15376        let tree = parser.finish_rule(child, false);
15377
15378        assert_eq!(parser.la(1), TOKEN_EOF);
15379        assert_eq!(
15380            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
15381            "(a z)"
15382        );
15383        assert_eq!(parser.number_of_syntax_errors(), 1);
15384        assert_eq!(
15385            parser.generated_parser_diagnostics,
15386            [ParserDiagnostic {
15387                line: 1,
15388                column: 0,
15389                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15390            }]
15391        );
15392        parser.exit_rule();
15393    }
15394
15395    #[test]
15396    fn greedy_ll1_alt_handles_nullable_loop_exit() {
15397        let mut body_symbols = TokenBitSet::default();
15398        body_symbols.insert(1);
15399        let entry = DecisionLookahead {
15400            transitions: vec![
15401                TransitionLookSet {
15402                    symbols: body_symbols,
15403                    nullable: false,
15404                },
15405                TransitionLookSet {
15406                    symbols: TokenBitSet::default(),
15407                    nullable: true,
15408                },
15409            ],
15410        };
15411
15412        assert_eq!(ll1_unique_alt(&entry, 2), None);
15413        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
15414        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
15415        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
15416    }
15417
15418    #[test]
15419    fn ordinary_repetition_builds_tree_in_input_order() {
15420        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15421            let mut parser = mini_parser(repeated_x_tokens(3));
15422            let tree = parser
15423                .parse_atn_rule(&atn, 0)
15424                .expect("ordinary repetition should parse");
15425
15426            let root = parser
15427                .node(tree)
15428                .as_rule()
15429                .expect("entry result should be a rule");
15430            let body_rules = root.child_rules(1).collect::<Vec<_>>();
15431            assert_eq!(root.text(), "xxx<EOF>");
15432            assert_eq!(body_rules.len(), 3);
15433            assert_eq!(
15434                body_rules
15435                    .iter()
15436                    .map(|rule| rule.start_id().expect("body start").index())
15437                    .collect::<Vec<_>>(),
15438                [0, 1, 2]
15439            );
15440            assert_eq!(
15441                body_rules
15442                    .iter()
15443                    .map(|rule| rule.stop_id().expect("body stop").index())
15444                    .collect::<Vec<_>>(),
15445                [0, 1, 2]
15446            );
15447            assert_eq!(parser.number_of_syntax_errors(), 0);
15448        }
15449    }
15450
15451    #[test]
15452    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
15453        const DEPTH: usize = 20_000;
15454
15455        std::thread::Builder::new()
15456            .name("deferred-rule-materialization".to_owned())
15457            .stack_size(256 * 1024)
15458            .spawn(|| {
15459                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15460                let mut root = FastDeferredNodeId::EMPTY;
15461                for depth in 0..DEPTH {
15462                    root = parser
15463                        .recognition_arena
15464                        .deferred_rule_node(FastDeferredRule {
15465                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
15466                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
15467                            start_index: 0,
15468                            stop_index: None,
15469                            deferred_children: root,
15470                            children: NodeSeqId::EMPTY,
15471                        });
15472                }
15473
15474                let mut children = parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
15475                for expected_rule in (0..DEPTH).rev() {
15476                    let mut nodes = parser.recognition_arena.iter(children);
15477                    let node = nodes.next().expect("nested rule node");
15478                    assert!(nodes.next().is_none(), "each rule has one child");
15479                    let ArenaRecognizedNode::Rule {
15480                        rule_index,
15481                        children: nested,
15482                        ..
15483                    } = parser.recognition_arena.node(node)
15484                    else {
15485                        panic!("expected nested rule");
15486                    };
15487                    assert_eq!(rule_index as usize, expected_rule);
15488                    children = nested;
15489                }
15490                assert!(children.is_empty());
15491            })
15492            .expect("small-stack thread should start")
15493            .join()
15494            .expect("deferred rules should materialize without recursion");
15495    }
15496
15497    #[test]
15498    fn deeply_nested_rule_calls_grow_the_stack() {
15499        const DEPTH: usize = 4_096;
15500        const STACK_SIZE: usize = 256 * 1024;
15501        let atn = nested_rule_chain_atn(DEPTH);
15502        std::thread::Builder::new()
15503            .name("nested-adaptive-set-rules".to_owned())
15504            .stack_size(STACK_SIZE)
15505            .spawn(move || {
15506                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
15507                parser.set_build_parse_trees(false);
15508                // This test isolates recognizer depth from the separately
15509                // cached FIRST-set metadata walk.
15510                parser.fast_first_set_prefilter = false;
15511                parser
15512                    .parse_atn_rule(&atn, 0)
15513                    .expect("nested rule chain should grow the native stack");
15514                assert_eq!(parser.input.index(), 1);
15515            })
15516            .expect("small-stack thread should start")
15517            .join()
15518            .expect("nested rule chain should not overflow its stack");
15519    }
15520
15521    #[test]
15522    fn deeply_nested_branching_rules_grow_the_stack() {
15523        const DEPTH: usize = 4_096;
15524        const STACK_SIZE: usize = 256 * 1024;
15525        let atn = nested_rule_graph_atn(DEPTH, true, false);
15526        std::thread::Builder::new()
15527            .name("nested-branching-rules".to_owned())
15528            .stack_size(STACK_SIZE)
15529            .spawn(move || {
15530                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
15531                parser.set_build_parse_trees(false);
15532                parser
15533                    .parse_atn_rule(&atn, 0)
15534                    .expect("branching rule chain should grow the native stack");
15535                assert_eq!(parser.input.index(), 1);
15536            })
15537            .expect("small-stack thread should start")
15538            .join()
15539            .expect("branching rule chain should not overflow its stack");
15540    }
15541
15542    #[test]
15543    fn deeply_nested_rule_follows_grow_the_stack() {
15544        const DEPTH: usize = 4_096;
15545        const STACK_SIZE: usize = 256 * 1024;
15546        let atn = nested_rule_graph_atn(DEPTH, false, true);
15547        std::thread::Builder::new()
15548            .name("nested-rule-follows".to_owned())
15549            .stack_size(STACK_SIZE)
15550            .spawn(move || {
15551                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
15552                parser.set_build_parse_trees(false);
15553                parser.fast_first_set_prefilter = false;
15554                parser
15555                    .parse_atn_rule(&atn, 0)
15556                    .expect("rule follow chain should grow the native stack");
15557                assert_eq!(parser.input.index(), DEPTH);
15558            })
15559            .expect("small-stack thread should start")
15560            .join()
15561            .expect("nested rule follow chain should not overflow its stack");
15562    }
15563
15564    #[test]
15565    fn deeply_nested_recovery_grows_the_stack() {
15566        const DEPTH: usize = 4_096;
15567        const STACK_SIZE: usize = 256 * 1024;
15568        let atn = nested_rule_chain_atn(DEPTH);
15569        std::thread::Builder::new()
15570            .name("nested-rule-recovery".to_owned())
15571            .stack_size(STACK_SIZE)
15572            .spawn(move || {
15573                let mut parser = mini_parser(vec![
15574                    TestToken::new(2).with_text("z"),
15575                    TestToken::new(1).with_text("x"),
15576                    TestToken::eof("parser-test", 2, 1, 2),
15577                ]);
15578                parser.set_build_parse_trees(false);
15579                parser.fast_first_set_prefilter = false;
15580                parser
15581                    .parse_atn_rule(&atn, 0)
15582                    .expect("nested recovery should grow the native stack");
15583                assert_eq!(parser.input.index(), 2);
15584                assert_eq!(parser.number_of_syntax_errors(), 1);
15585            })
15586            .expect("small-stack thread should start")
15587            .join()
15588            .expect("nested rule recovery should not overflow its stack");
15589    }
15590
15591    #[test]
15592    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
15593        const REPETITIONS: usize = 64;
15594
15595        let atn = ambiguous_ordinary_star_loop_atn();
15596        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15597        let tree = parser
15598            .parse_atn_rule(&atn, 0)
15599            .expect("ambiguous ordinary repetition should parse");
15600
15601        let root = parser
15602            .node(tree)
15603            .as_rule()
15604            .expect("entry result should be a rule");
15605        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
15606        assert_eq!(parser.input.index(), REPETITIONS);
15607        assert!(
15608            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
15609            "equivalent segmentations should keep deferred storage linear"
15610        );
15611        assert_eq!(parser.number_of_syntax_errors(), 0);
15612    }
15613
15614    #[test]
15615    fn long_ordinary_repetition_does_not_consume_native_stack() {
15616        const REPETITIONS: usize = 20_000;
15617
15618        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15619            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15620            parser.set_build_parse_trees(false);
15621            parser
15622                .parse_atn_rule(&atn, 0)
15623                .expect("long ordinary repetition should parse");
15624
15625            assert_eq!(parser.input.index(), REPETITIONS);
15626            assert_eq!(parser.number_of_syntax_errors(), 0);
15627        }
15628    }
15629
15630    #[test]
15631    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
15632        const REPETITIONS: usize = 2_000;
15633        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
15634
15635        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15636            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15637            let tree = parser
15638                .parse_atn_rule(&atn, 0)
15639                .expect("long rule repetition should parse");
15640
15641            let root = parser
15642                .node(tree)
15643                .as_rule()
15644                .expect("entry result should be a rule");
15645            assert_eq!(root.text(), expected_text);
15646            assert_eq!(root.child_rules(1).count(), REPETITIONS);
15647            let first_body = root.child_rules(1).next().expect("first body rule");
15648            let last_body = root.child_rules(1).next_back().expect("last body rule");
15649            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
15650            assert_eq!(
15651                last_body.stop_id().expect("last body stop").index(),
15652                REPETITIONS - 1
15653            );
15654
15655            let stats = parser.recognition_arena_stats();
15656            assert_eq!(
15657                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
15658                (REPETITIONS, REPETITIONS, 0)
15659            );
15660            assert_eq!(
15661                (stats.total_links, stats.live_links, stats.dead_links),
15662                (REPETITIONS, REPETITIONS, 0)
15663            );
15664            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
15665            assert_eq!(
15666                parser.recognition_arena.deferred_nodes.len(),
15667                REPETITIONS * 2 - 1
15668            );
15669            assert_eq!(parser.number_of_syntax_errors(), 0);
15670        }
15671    }
15672
15673    #[test]
15674    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
15675        let key = |state_number| FastRecognizeKey {
15676            state_number,
15677            stop_state: 10,
15678            index: state_number,
15679            rule_start_index: 0,
15680            decision_start_index: None,
15681            precedence: 0,
15682            recovery_symbols_id: 0,
15683            recovery_state: None,
15684        };
15685
15686        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15687        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
15688            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
15689        }
15690        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
15691        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
15692
15693        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15694        let repeated = key(1);
15695        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
15696            assert!(promote.clean_memo_enabled_for_key(&repeated));
15697        }
15698        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
15699
15700        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
15701            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
15702        }
15703        assert!(sparse.clean_memo_enabled_for_key(&repeated));
15704        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
15705        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
15706            assert!(sparse.clean_memo_enabled_for_key(&repeated));
15707        }
15708        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
15709    }
15710
15711    #[test]
15712    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
15713        assert_eq!(
15714            fast_recognize_memo_capacity(0),
15715            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15716        );
15717        assert_eq!(
15718            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
15719            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15720        );
15721        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
15722        assert_eq!(
15723            fast_recognize_memo_capacity(usize::MAX),
15724            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
15725        );
15726    }
15727
15728    #[test]
15729    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
15730        let mut scratch = FastRecognizeTopScratch::default();
15731        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15732        let retained_capacity = scratch.memo.capacity();
15733        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15734        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15735
15736        let larger_capacity = retained_capacity + 1;
15737        scratch.prepare(larger_capacity);
15738        let grown_capacity = scratch.memo.capacity();
15739        assert!(grown_capacity >= larger_capacity);
15740        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15741
15742        scratch.memo.insert(
15743            FastRecognizeKey {
15744                state_number: 0,
15745                stop_state: 0,
15746                index: 0,
15747                rule_start_index: 0,
15748                decision_start_index: None,
15749                precedence: 0,
15750                recovery_symbols_id: 0,
15751                recovery_state: None,
15752            },
15753            Rc::from([FastRecognizeOutcome {
15754                index: 0,
15755                consumed_eof: false,
15756                diagnostics: DiagnosticSeqId::EMPTY,
15757                deferred_nodes: FastDeferredNodeId::EMPTY,
15758                nodes: NodeSeqId::EMPTY,
15759            }]),
15760        );
15761        scratch.release_oversized_memo();
15762        assert!(scratch.memo.is_empty());
15763        assert_eq!(scratch.memo.capacity(), grown_capacity);
15764
15765        scratch
15766            .memo
15767            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
15768        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15769
15770        scratch.release_oversized_memo();
15771        assert!(scratch.memo.is_empty());
15772        assert_eq!(scratch.memo.capacity(), 0);
15773    }
15774
15775    #[test]
15776    fn clean_empty_multi_alt_outcomes_are_memoized() {
15777        let mut atn = ParserAtnBuilder::new(2);
15778        assert_eq!(
15779            atn.add_state(AtnStateKind::RuleStart, Some(0))
15780                .expect("state")
15781                .index(),
15782            0
15783        );
15784        assert_eq!(
15785            atn.add_state(AtnStateKind::BlockStart, Some(0))
15786                .expect("state")
15787                .index(),
15788            1
15789        );
15790        assert_eq!(
15791            atn.add_state(AtnStateKind::RuleStop, Some(0))
15792                .expect("state")
15793                .index(),
15794            2
15795        );
15796        atn.set_rule_to_start_state(vec![0])
15797            .expect("rule start states");
15798        atn.set_rule_to_stop_state(vec![2])
15799            .expect("rule stop states");
15800        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15801            .expect("transition");
15802        atn.add_transition(
15803            1,
15804            ParserTransitionSpec::Atom {
15805                target: 2,
15806                label: 1,
15807            },
15808        )
15809        .expect("transition");
15810        atn.add_transition(
15811            1,
15812            ParserTransitionSpec::Atom {
15813                target: 2,
15814                label: 2,
15815            },
15816        )
15817        .expect("transition");
15818        let atn = finish_atn(atn);
15819
15820        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15821        parser.fast_recovery_enabled = false;
15822        let mut visiting = FxHashSet::default();
15823        let mut memo = FxHashMap::default();
15824        let mut expected = ExpectedTokens::default();
15825        let outcomes = parser.recognize_state_fast(
15826            &atn,
15827            FastRecognizeRequest {
15828                state_number: 1,
15829                stop_state: 2,
15830                index: 0,
15831                rule_start_index: 0,
15832                decision_start_index: None,
15833                precedence: 0,
15834                depth: 0,
15835                recovery_symbols: parser.empty_recovery_symbols(),
15836                recovery_state: None,
15837            },
15838            FastRecognizeScratch {
15839                predicate_context: None,
15840                visiting: &mut visiting,
15841                memo: &mut memo,
15842                expected: &mut expected,
15843                native_depth: 0,
15844            },
15845        );
15846
15847        assert!(outcomes.is_empty());
15848        assert_eq!(memo.len(), 1);
15849        assert!(memo.values().next().expect("memo entry").is_empty());
15850
15851        parser.clean_memo_mode = CleanMemoMode::Sparse;
15852        visiting.clear();
15853        memo.clear();
15854        expected = ExpectedTokens::default();
15855        let sparse_outcomes = parser.recognize_state_fast(
15856            &atn,
15857            FastRecognizeRequest {
15858                state_number: 1,
15859                stop_state: 2,
15860                index: 0,
15861                rule_start_index: 0,
15862                decision_start_index: None,
15863                precedence: 0,
15864                depth: 0,
15865                recovery_symbols: parser.empty_recovery_symbols(),
15866                recovery_state: None,
15867            },
15868            FastRecognizeScratch {
15869                predicate_context: None,
15870                visiting: &mut visiting,
15871                memo: &mut memo,
15872                expected: &mut expected,
15873                native_depth: 0,
15874            },
15875        );
15876
15877        assert!(sparse_outcomes.is_empty());
15878        assert!(memo.is_empty());
15879    }
15880
15881    #[test]
15882    fn wildcard_matches_non_eof_only() {
15883        let mut parser = mini_parser(vec![
15884            TestToken::new(1).with_text("x"),
15885            TestToken::eof("parser-test", 1, 1, 1),
15886        ]);
15887        let matched = parser.match_wildcard().expect("wildcard");
15888        assert_eq!(parser.node(matched).text(), "x");
15889        assert!(parser.match_wildcard().is_err());
15890    }
15891
15892    #[test]
15893    fn add_parse_child_records_match_even_without_tree_building() {
15894        // `sync_decision`'s "is the current context empty" flag must reflect real
15895        // matches, not parse-tree children: when `build_parse_trees(false)`,
15896        // `children` stays empty but `has_matched_child` must still flip so nested
15897        // recovery does not wrongly suppress single-token deletion.
15898        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15899        let token = TestToken::new(1).with_text("x");
15900
15901        parser.set_build_parse_trees(false);
15902        let mut ctx = ParserRuleContext::new(0, 0);
15903        assert!(!ctx.has_matched_child());
15904        let child = parser.terminal_tree(token.id);
15905        parser.add_parse_child(&mut ctx, child);
15906        // Tree building is off, so no child is stored...
15907        assert_eq!(ctx.child_count(), 0);
15908        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15909        // ...but the match is recorded, so the context is no longer "empty".
15910        assert!(ctx.has_matched_child());
15911
15912        // With tree building on, the child is stored and the match is recorded.
15913        parser.set_build_parse_trees(true);
15914        let mut ctx = ParserRuleContext::new(0, 0);
15915        let child = parser.terminal_tree(token.id);
15916        parser.add_parse_child(&mut ctx, child);
15917        assert_eq!(ctx.child_count(), 1);
15918        assert!(ctx.has_matched_child());
15919    }
15920
15921    #[test]
15922    fn disabled_tree_building_does_not_grow_flat_storage() {
15923        let mut parser = mini_parser(vec![
15924            TestToken::new(1).with_text("x"),
15925            TestToken::new(1).with_text("y"),
15926            TestToken::eof("parser-test", 2, 1, 2),
15927        ]);
15928        parser.set_build_parse_trees(false);
15929        let mut context = ParserRuleContext::new(0, -1);
15930
15931        for _ in 0..2 {
15932            let child = parser.match_token(1).expect("token should match");
15933            parser.add_parse_child(&mut context, child);
15934        }
15935        let current = parser.input.lt_id(1).expect("EOF token");
15936        let error = parser.error_tree(current);
15937        parser.add_parse_child(&mut context, error);
15938        let root = parser.rule_node(context);
15939
15940        assert_eq!(
15941            parser.parse_tree_storage().stats(),
15942            ParseTreeStats::default()
15943        );
15944        assert!(
15945            parser
15946                .parse_tree_storage()
15947                .node(parser.token_store(), root)
15948                .is_none(),
15949            "the no-tree sentinel must not resolve to stored data"
15950        );
15951    }
15952
15953    #[test]
15954    fn disabled_tree_building_skips_recognition_rule_node_storage() {
15955        let atn = ordinary_star_loop_atn();
15956        let mut parser = mini_parser(repeated_x_tokens(3));
15957        parser.set_build_parse_trees(false);
15958
15959        parser
15960            .parse_atn_rule(&atn, 0)
15961            .expect("ordinary repetition should parse without a tree");
15962
15963        assert_eq!(parser.input.index(), 3);
15964        assert!(parser.recognition_arena.nodes.is_empty());
15965        assert!(parser.recognition_arena.seq_links.is_empty());
15966        assert!(parser.recognition_arena.deferred_nodes.is_empty());
15967        assert!(parser.recognition_arena.deferred_rules.is_empty());
15968        assert!(!parser.fast_token_nodes_enabled);
15969        assert!(parser.fast_recognize_scratch.memo.is_empty());
15970    }
15971
15972    #[test]
15973    fn parser_interprets_simple_atn_rule() {
15974        let atn = token_then_eof_atn();
15975        let mut parser = mini_parser(vec![
15976            TestToken::new(1).with_text("x"),
15977            TestToken::eof("parser-test", 1, 1, 1),
15978        ]);
15979
15980        let tree = parser
15981            .parse_atn_rule(&atn, 0)
15982            .expect("artificial parser rule should parse");
15983        assert_eq!(parser.node(tree).text(), "x<EOF>");
15984        assert_eq!(parser.number_of_syntax_errors(), 0);
15985        assert_eq!(
15986            parser
15987                .node(tree)
15988                .first_rule_stop(0)
15989                .expect("rule should stop at EOF")
15990                .token_type(),
15991            TOKEN_EOF
15992        );
15993
15994        let mut parser = mini_parser(vec![
15995            TestToken::new(1).with_text("x"),
15996            TestToken::eof("parser-test", 1, 1, 1),
15997        ]);
15998        let (tree, actions) = parser
15999            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16000            .expect("runtime-option parser rule should parse");
16001        assert!(actions.is_empty());
16002        assert_eq!(
16003            parser
16004                .node(tree)
16005                .first_rule_stop(0)
16006                .expect("rule should stop at EOF")
16007                .token_type(),
16008            TOKEN_EOF
16009        );
16010    }
16011
16012    #[test]
16013    fn runtime_options_default_ignores_noop_action_transitions() {
16014        let atn = noop_action_then_token_then_eof_atn();
16015        let mut parser = mini_parser(vec![
16016            TestToken::new(1).with_text("x"),
16017            TestToken::eof("parser-test", 1, 1, 1),
16018        ]);
16019
16020        let (tree, actions) = parser
16021            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16022            .expect("no-op parser action should not force action replay");
16023
16024        assert_eq!(parser.node(tree).text(), "x<EOF>");
16025        assert!(
16026            actions.is_empty(),
16027            "action_index=None transitions are ANTLR metadata, not replay actions"
16028        );
16029        assert_eq!(parser.number_of_syntax_errors(), 0);
16030    }
16031
16032    #[test]
16033    fn parser_exposes_buffered_token_stream_after_parse() {
16034        let atn = token_then_eof_atn();
16035        let mut parser = mini_parser(vec![
16036            TestToken::new(1).with_text("x"),
16037            TestToken::eof("parser-test", 1, 1, 1),
16038        ]);
16039
16040        let tree = parser
16041            .parse_atn_rule(&atn, 0)
16042            .expect("artificial parser rule should parse");
16043        assert_eq!(parser.node(tree).text(), "x<EOF>");
16044
16045        let stream = parser.token_stream();
16046        let source_index_after_parse = stream.token_source().index;
16047        let buffered = stream.tokens().collect::<Vec<_>>();
16048        assert_eq!(buffered.len(), 2);
16049        assert_eq!(buffered[0].text(), "x");
16050        assert_eq!(buffered[0].token_id().index(), 0);
16051        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
16052        assert_eq!(stream.token_source().index, source_index_after_parse);
16053        drop(buffered);
16054
16055        let stream = parser.into_token_stream();
16056        assert_eq!(stream.token_source().index, source_index_after_parse);
16057        assert_eq!(stream.tokens().next().expect("first token").text(), "x");
16058        assert_eq!(
16059            stream.tokens().nth(1).expect("EOF token").token_type(),
16060            TOKEN_EOF
16061        );
16062    }
16063
16064    #[test]
16065    fn parser_syntax_error_count_tracks_interpreted_recovery() {
16066        let atn = token_then_eof_atn();
16067        let mut parser = mini_parser(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
16073        let tree = parser
16074            .parse_atn_rule(&atn, 0)
16075            .expect("invalid token should recover into an error node");
16076
16077        assert_eq!(parser.number_of_syntax_errors(), 1);
16078        assert_eq!(
16079            parser
16080                .node(tree)
16081                .first_error_token()
16082                .expect("recovery should embed an error token")
16083                .text(),
16084            "y"
16085        );
16086    }
16087
16088    #[test]
16089    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
16090        let atn = token_then_eof_atn();
16091        let mut parser = mini_parser(vec![
16092            TestToken::new(2).with_text("y"),
16093            TestToken::eof("parser-test", 1, 1, 1),
16094        ]);
16095
16096        let error = parser
16097            .parse_atn_rule(&atn, 0)
16098            .expect_err("start-rule mismatch should remain a parser error");
16099
16100        assert_eq!(parser.number_of_syntax_errors(), 1);
16101        assert!(matches!(error, AntlrError::ParserError { .. }));
16102    }
16103
16104    #[test]
16105    fn adaptive_direct_rule_uses_simulator_decision() {
16106        let atn = two_alt_decision_atn();
16107        let mut simulator = ParserAtnSimulator::new(&atn);
16108        let mut parser = mini_parser(vec![
16109            TestToken::new(2).with_text("y"),
16110            TestToken::eof("parser-test", 1, 1, 1),
16111        ]);
16112
16113        let tree = parser
16114            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
16115            .expect("direct adaptive rule should parse");
16116
16117        assert_eq!(parser.node(tree).text(), "y");
16118        assert_eq!(parser.input.index(), 1);
16119    }
16120
16121    #[test]
16122    fn adaptive_direct_rule_restores_input_on_fallback() {
16123        let atn = predicate_after_token_atn();
16124        let mut simulator = ParserAtnSimulator::new(&atn);
16125        let mut parser = mini_parser(vec![
16126            TestToken::new(1).with_text("x"),
16127            TestToken::new(2).with_text("y"),
16128            TestToken::eof("parser-test", 2, 1, 2),
16129        ]);
16130
16131        let tree = parser
16132            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
16133            .expect("fallback recognizer should parse");
16134
16135        assert_eq!(parser.node(tree).text(), "xy");
16136        assert_eq!(parser.input.index(), 2);
16137        let stats = parser.parse_tree_storage().stats();
16138        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
16139        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
16140        assert_eq!(stats.scratch_links, 0);
16141    }
16142
16143    #[test]
16144    fn unknown_predicate_policy_defaults_to_assume_true() {
16145        let atn = predicate_after_token_atn();
16146        let mut parser = mini_parser(vec![
16147            TestToken::new(1).with_text("x"),
16148            TestToken::new(2).with_text("y"),
16149            TestToken::eof("parser-test", 2, 1, 2),
16150        ]);
16151
16152        let (tree, _) = parser
16153            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16154            .expect("unknown predicate should pass under the default policy");
16155
16156        assert_eq!(parser.node(tree).text(), "xy");
16157        assert_eq!(parser.number_of_syntax_errors(), 0);
16158    }
16159
16160    #[test]
16161    fn predicate_gated_same_lookahead_uses_viable_alternative() {
16162        let atn = predicate_gated_same_lookahead_atn([0, 1]);
16163        let mut parser = mini_parser(vec![
16164            TestToken::new(1).with_text("x"),
16165            TestToken::eof("parser-test", 1, 1, 1),
16166        ]);
16167
16168        let (tree, _) = parser
16169            .parse_atn_rule_with_runtime_options(
16170                &atn,
16171                0,
16172                ParserRuntimeOptions {
16173                    predicates: &[
16174                        (0, 0, ParserPredicate::False),
16175                        (0, 1, ParserPredicate::True),
16176                    ],
16177                    ..ParserRuntimeOptions::default()
16178                },
16179            )
16180            .expect("the second predicate-gated alternative should match");
16181
16182        assert_eq!(parser.node(tree).text(), "x<EOF>");
16183        assert_eq!(parser.number_of_syntax_errors(), 0);
16184        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
16185        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
16186    }
16187
16188    #[test]
16189    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
16190        // A generated parent may record an unknown-predicate coordinate, then
16191        // descend into an interpreted child. The child's interpreter entry must
16192        // not wipe the parent's recorded hit before the top-level surfaces it.
16193        let atn = token_then_eof_atn();
16194        let mut parser = mini_parser(vec![
16195            TestToken::new(1).with_text("x"),
16196            TestToken::eof("parser-test", 1, 1, 1),
16197        ]);
16198
16199        // Simulate the parent having recorded a fail-loud coordinate.
16200        parser.unknown_predicate_hits.push((7, 3));
16201
16202        // Run an interpreted child parse that records no coordinate of its own.
16203        parser
16204            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16205            .expect("child rule parses");
16206
16207        // The parent's coordinate must still be present for the top-level entry.
16208        let error = parser
16209            .take_unknown_semantic_error()
16210            .expect("parent's recorded coordinate must survive the nested interpreted parse");
16211        let AntlrError::Unsupported(message) = error else {
16212            panic!("expected AntlrError::Unsupported, got {error:?}");
16213        };
16214        assert!(message.contains("pred_index=3"), "message: {message}");
16215    }
16216
16217    #[test]
16218    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
16219        let atn = predicate_after_token_atn();
16220        let mut parser = mini_parser(vec![
16221            TestToken::new(1).with_text("x"),
16222            TestToken::new(2).with_text("y"),
16223            TestToken::eof("parser-test", 2, 1, 2),
16224        ]);
16225
16226        let result = parser.parse_atn_rule_with_runtime_options(
16227            &atn,
16228            0,
16229            ParserRuntimeOptions {
16230                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16231                ..ParserRuntimeOptions::default()
16232            },
16233        );
16234
16235        assert!(
16236            result.is_err(),
16237            "the only path is predicate-guarded, so assume-false must fail the parse"
16238        );
16239    }
16240
16241    #[test]
16242    fn predicate_failure_message_keeps_semantic_recovery_path() {
16243        let atn = predicate_after_token_atn();
16244        let mut parser = mini_parser(vec![
16245            TestToken::new(1).with_text("x"),
16246            TestToken::new(2).with_text("y"),
16247            TestToken::eof("parser-test", 2, 1, 2),
16248        ]);
16249
16250        let (tree, _) = parser
16251            .parse_atn_rule_with_runtime_options(
16252                &atn,
16253                0,
16254                ParserRuntimeOptions {
16255                    predicates: &[(
16256                        0,
16257                        0,
16258                        ParserPredicate::FalseWithMessage {
16259                            message: "predicate rejected input",
16260                        },
16261                    )],
16262                    ..ParserRuntimeOptions::default()
16263                },
16264            )
16265            .expect("failure-message predicates recover through the semantic interpreter");
16266
16267        assert_eq!(parser.node(tree).text(), "xy");
16268        assert_eq!(parser.number_of_syntax_errors(), 1);
16269        assert!(
16270            parser.fast_predicate_cache.is_empty(),
16271            "failure-message predicates need the semantic interpreter's recovery outcome"
16272        );
16273    }
16274
16275    #[test]
16276    fn unknown_predicate_policy_error_names_the_coordinate() {
16277        let atn = predicate_after_token_atn();
16278        let mut parser = mini_parser(vec![
16279            TestToken::new(1).with_text("x"),
16280            TestToken::new(2).with_text("y"),
16281            TestToken::eof("parser-test", 2, 1, 2),
16282        ]);
16283
16284        let error = parser
16285            .parse_atn_rule_with_runtime_options(
16286                &atn,
16287                0,
16288                ParserRuntimeOptions {
16289                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16290                    ..ParserRuntimeOptions::default()
16291                },
16292            )
16293            .expect_err("evaluating an unknown predicate under Error policy must fail");
16294
16295        let AntlrError::Unsupported(message) = error else {
16296            panic!("expected AntlrError::Unsupported, got {error:?}");
16297        };
16298        assert!(
16299            message.contains("unsupported semantic predicate"),
16300            "message should name the failure class: {message}"
16301        );
16302        assert!(
16303            message.contains("pred_index=0"),
16304            "message should carry the coordinate: {message}"
16305        );
16306    }
16307
16308    #[test]
16309    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
16310        // A parser reused after a fail-loud parse must not carry the old
16311        // coordinates into a later parse. The fail-loud return keeps the hits
16312        // (so a generated parent can surface a recovered child's coordinate),
16313        // and the next parse's entry stashes/replaces them, so a subsequent
16314        // clean parse surfaces no stale error.
16315        let atn = predicate_after_token_atn();
16316        let mut parser = mini_parser(vec![
16317            TestToken::new(1).with_text("x"),
16318            TestToken::new(2).with_text("y"),
16319            TestToken::eof("parser-test", 2, 1, 2),
16320        ]);
16321
16322        parser
16323            .parse_atn_rule_with_runtime_options(
16324                &atn,
16325                0,
16326                ParserRuntimeOptions {
16327                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16328                    ..ParserRuntimeOptions::default()
16329                },
16330            )
16331            .expect_err("first parse fails loud under the Error policy");
16332
16333        // The failed parse kept its coordinate on the parser (so a generated
16334        // parent could surface a recovered child). A top-level reuse resets the
16335        // hits — generated parsers call `reset_unknown_semantic_hits` at their
16336        // public entry; direct interpreter-API callers do the same.
16337        parser.reset_unknown_semantic_hits();
16338        assert!(
16339            parser.take_unknown_semantic_error().is_none(),
16340            "reset must drop stale unknown-predicate coordinates before a reused parse"
16341        );
16342    }
16343
16344    #[derive(Debug, Default)]
16345    struct RecordingHooks {
16346        predicates: Vec<(usize, usize, usize, Option<String>)>,
16347        actions: Vec<(usize, String, Option<String>)>,
16348        action_trees: Vec<Option<String>>,
16349    }
16350
16351    impl SemanticHooks for RecordingHooks {
16352        fn sempred<S>(
16353            &mut self,
16354            ctx: &mut ParserSemCtx<'_, S>,
16355            rule_index: usize,
16356            pred_index: usize,
16357        ) -> Option<bool>
16358        where
16359            S: TokenSource,
16360        {
16361            self.predicates.push((
16362                ctx.input_index(),
16363                rule_index,
16364                pred_index,
16365                ctx.token_text(1).map(|token| token.text().to_owned()),
16366            ));
16367            Some(true)
16368        }
16369
16370        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
16371        where
16372            S: TokenSource,
16373        {
16374            self.actions.push((
16375                action.source_state(),
16376                ctx.action_text(),
16377                ctx.rule_name().map(str::to_owned),
16378            ));
16379            self.action_trees.push(ctx.tree().map(Node::text));
16380            true
16381        }
16382    }
16383
16384    #[derive(Debug, Default)]
16385    struct RejectingPredicateHooks {
16386        predicates: Vec<(usize, usize, usize, Option<String>)>,
16387    }
16388
16389    impl SemanticHooks for RejectingPredicateHooks {
16390        fn sempred<S>(
16391            &mut self,
16392            ctx: &mut ParserSemCtx<'_, S>,
16393            rule_index: usize,
16394            pred_index: usize,
16395        ) -> Option<bool>
16396        where
16397            S: TokenSource,
16398        {
16399            self.predicates.push((
16400                ctx.input_index(),
16401                rule_index,
16402                pred_index,
16403                ctx.token_text(1).map(|token| token.text().to_owned()),
16404            ));
16405            Some(false)
16406        }
16407    }
16408
16409    #[test]
16410    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
16411        let atn = predicate_gated_same_lookahead_atn([0, 0]);
16412        let mut parser = mini_parser_with_hooks(
16413            vec![
16414                TestToken::new(1).with_text("x"),
16415                TestToken::eof("parser-test", 1, 1, 1),
16416            ],
16417            RecordingHooks::default(),
16418        );
16419
16420        let (tree, _) = parser
16421            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16422            .expect("both alternatives share one replay-safe predicate result");
16423
16424        assert_eq!(parser.node(tree).text(), "x<EOF>");
16425        assert_eq!(
16426            parser.semantic_hooks.predicates,
16427            vec![(0, 0, 0, Some("x".to_owned()))]
16428        );
16429        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
16430    }
16431
16432    #[test]
16433    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
16434        let atn = predicate_after_token_atn();
16435        let mut parser = mini_parser_with_hooks(
16436            vec![
16437                TestToken::new(1).with_text("x"),
16438                TestToken::new(2).with_text("y"),
16439                TestToken::eof("parser-test", 2, 1, 2),
16440            ],
16441            RecordingHooks::default(),
16442        );
16443
16444        let (tree, _) = parser
16445            .parse_atn_rule_with_runtime_options(
16446                &atn,
16447                0,
16448                ParserRuntimeOptions {
16449                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16450                    ..ParserRuntimeOptions::default()
16451                },
16452            )
16453            .expect("hook supplies the missing predicate result");
16454
16455        assert_eq!(parser.node(tree).text(), "xy");
16456        assert_eq!(
16457            parser.semantic_hooks.predicates,
16458            vec![(1, 0, 0, Some("y".to_owned()))]
16459        );
16460        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
16461    }
16462
16463    #[test]
16464    fn runtime_options_default_preserves_semantic_hook_predicates() {
16465        let atn = predicate_after_token_atn();
16466        let mut parser = mini_parser_with_hooks(
16467            vec![
16468                TestToken::new(1).with_text("x"),
16469                TestToken::new(2).with_text("y"),
16470                TestToken::eof("parser-test", 2, 1, 2),
16471            ],
16472            RejectingPredicateHooks::default(),
16473        );
16474
16475        let result =
16476            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
16477
16478        assert!(
16479            result.is_err(),
16480            "default runtime options must not bypass semantic hooks for predicate ATNs"
16481        );
16482        assert_eq!(
16483            parser.semantic_hooks.predicates,
16484            vec![(1, 0, 0, Some("y".to_owned()))]
16485        );
16486        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
16487    }
16488
16489    #[test]
16490    fn semantic_hook_handles_committed_parser_action() {
16491        let atn = token_then_eof_atn();
16492        let mut parser = mini_parser_with_hooks(
16493            vec![
16494                TestToken::new(1).with_text("x"),
16495                TestToken::eof("parser-test", 1, 1, 1),
16496            ],
16497            RecordingHooks::default(),
16498        );
16499        let (tree, _) = parser
16500            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16501            .expect("rule parses before action hook is tested");
16502
16503        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16504        assert_eq!(
16505            parser.semantic_hooks.actions,
16506            vec![(42, "x".to_owned(), Some("s".to_owned()))]
16507        );
16508        assert_eq!(
16509            parser.semantic_hooks.action_trees,
16510            [Some("x<EOF>".to_owned())]
16511        );
16512    }
16513
16514    #[test]
16515    fn unhandled_committed_action_fails_loud_under_error_policy() {
16516        // An action offered to the hook that no hook handles (returns false)
16517        // must be recorded and surfaced as `AntlrError::Unsupported` under the
16518        // Error policy, so a `hook`-disposed action is not silently dropped.
16519        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16520        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16521        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
16522
16523        // DecliningHooks::action returns false (unhandled).
16524        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16525
16526        let error = parser
16527            .take_unknown_semantic_error()
16528            .expect("an unhandled committed action under Error policy must fail loud");
16529        let AntlrError::Unsupported(message) = error else {
16530            panic!("expected AntlrError::Unsupported, got {error:?}");
16531        };
16532        assert!(
16533            message.contains("unhandled semantic action") && message.contains("state=42"),
16534            "message should name the dropped action coordinate: {message}"
16535        );
16536
16537        // Under the default (assume-true) policy the same miss is not recorded.
16538        let mut lenient =
16539            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16540        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
16541        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16542        assert!(lenient.take_unknown_semantic_error().is_none());
16543    }
16544
16545    #[test]
16546    fn translated_predicate_is_unaffected_by_error_policy() {
16547        let atn = predicate_after_token_atn();
16548        let mut parser = mini_parser(vec![
16549            TestToken::new(1).with_text("x"),
16550            TestToken::new(2).with_text("y"),
16551            TestToken::eof("parser-test", 2, 1, 2),
16552        ]);
16553
16554        let (tree, _) = parser
16555            .parse_atn_rule_with_runtime_options(
16556                &atn,
16557                0,
16558                ParserRuntimeOptions {
16559                    predicates: &[(0, 0, ParserPredicate::True)],
16560                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16561                    ..ParserRuntimeOptions::default()
16562                },
16563            )
16564            .expect("a predicate covered by the table is not an unknown coordinate");
16565
16566        assert_eq!(parser.node(tree).text(), "xy");
16567    }
16568
16569    /// Hooks that decline (`None`) must fall through to the configured policy
16570    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
16571    /// legacy table path. Regression for the `unwrap_or(false)` that silently
16572    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
16573    fn hook_predicate_semantics() -> ParserSemantics {
16574        let mut ir = SemIr::new();
16575        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
16576        ParserSemantics {
16577            ir,
16578            predicates: vec![ParserSemanticPredicate {
16579                rule_index: 0,
16580                pred_index: 0,
16581                expr,
16582                failure_message: None,
16583            }],
16584            actions: Vec::new(),
16585        }
16586    }
16587
16588    #[derive(Debug, Default)]
16589    struct DecliningHooks;
16590
16591    impl SemanticHooks for DecliningHooks {}
16592
16593    #[test]
16594    fn semir_hook_none_falls_through_to_assume_true() {
16595        let atn = predicate_after_token_atn();
16596        let semantics = hook_predicate_semantics();
16597        let mut parser = mini_parser_with_hooks(
16598            vec![
16599                TestToken::new(1).with_text("x"),
16600                TestToken::new(2).with_text("y"),
16601                TestToken::eof("parser-test", 2, 1, 2),
16602            ],
16603            DecliningHooks,
16604        );
16605
16606        let (tree, _) = parser
16607            .parse_atn_rule_with_runtime_options(
16608                &atn,
16609                0,
16610                ParserRuntimeOptions {
16611                    semantics: Some(&semantics),
16612                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
16613                    ..ParserRuntimeOptions::default()
16614                },
16615            )
16616            .expect("a declined SemIR hook must pass under assume-true");
16617
16618        assert_eq!(parser.node(tree).text(), "xy");
16619    }
16620
16621    #[test]
16622    fn semir_hook_none_falls_through_to_assume_false() {
16623        let atn = predicate_after_token_atn();
16624        let semantics = hook_predicate_semantics();
16625        let mut parser = mini_parser_with_hooks(
16626            vec![
16627                TestToken::new(1).with_text("x"),
16628                TestToken::new(2).with_text("y"),
16629                TestToken::eof("parser-test", 2, 1, 2),
16630            ],
16631            DecliningHooks,
16632        );
16633
16634        let result = parser.parse_atn_rule_with_runtime_options(
16635            &atn,
16636            0,
16637            ParserRuntimeOptions {
16638                semantics: Some(&semantics),
16639                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16640                ..ParserRuntimeOptions::default()
16641            },
16642        );
16643
16644        assert!(
16645            result.is_err(),
16646            "a declined SemIR hook must fail the only guarded path under assume-false"
16647        );
16648    }
16649
16650    #[test]
16651    fn semir_hook_none_records_coordinate_under_error_policy() {
16652        let atn = predicate_after_token_atn();
16653        let semantics = hook_predicate_semantics();
16654        let mut parser = mini_parser_with_hooks(
16655            vec![
16656                TestToken::new(1).with_text("x"),
16657                TestToken::new(2).with_text("y"),
16658                TestToken::eof("parser-test", 2, 1, 2),
16659            ],
16660            DecliningHooks,
16661        );
16662
16663        let error = parser
16664            .parse_atn_rule_with_runtime_options(
16665                &atn,
16666                0,
16667                ParserRuntimeOptions {
16668                    semantics: Some(&semantics),
16669                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16670                    ..ParserRuntimeOptions::default()
16671                },
16672            )
16673            .expect_err("a declined SemIR hook under Error policy must fail the parse");
16674
16675        let AntlrError::Unsupported(message) = error else {
16676            panic!("expected AntlrError::Unsupported, got {error:?}");
16677        };
16678        assert!(
16679            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
16680            "message should name the unresolved coordinate: {message}"
16681        );
16682    }
16683
16684    #[test]
16685    fn generated_direct_predicate_honors_installed_policy() {
16686        // The generated recursive-descent path calls
16687        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
16688        // going through `ParserRuntimeOptions`, so the policy must be installed
16689        // via `set_unknown_predicate_policy` (as the generated constructor now
16690        // does). A declining hook must then honor it rather than the default.
16691        let semantics = hook_predicate_semantics();
16692        let context = ParserRuleContext::new(0, -1);
16693
16694        let mut assume_true =
16695            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16696        assert!(
16697            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
16698                &semantics, 0, 0, &context, 0
16699            ),
16700            "default AssumeTrue accepts a declined hook"
16701        );
16702        assert!(assume_true.take_unknown_semantic_error().is_none());
16703
16704        let mut error_policy =
16705            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16706        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16707        assert!(
16708            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
16709                &semantics, 0, 0, &context, 0
16710            ),
16711            "Error policy rejects a declined hook on the generated-direct path"
16712        );
16713        let error = error_policy
16714            .take_unknown_semantic_error()
16715            .expect("Error policy records the unresolved coordinate for the generated path");
16716        let AntlrError::Unsupported(message) = error else {
16717            panic!("expected AntlrError::Unsupported, got {error:?}");
16718        };
16719        assert!(message.contains("pred_index=0"), "message: {message}");
16720    }
16721
16722    #[test]
16723    fn parser_rule_start_skips_leading_hidden_tokens() {
16724        let atn = token_then_eof_atn();
16725        let mut parser = mini_parser(vec![
16726            TestToken::new(99)
16727                .with_text(" ")
16728                .with_channel(HIDDEN_CHANNEL),
16729            TestToken::new(1).with_text("x"),
16730            TestToken::eof("parser-test", 2, 1, 2),
16731        ]);
16732
16733        let tree = parser
16734            .parse_atn_rule(&atn, 0)
16735            .expect("artificial parser rule should parse");
16736        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
16737            panic!("rule node should be present");
16738        };
16739        assert_eq!(
16740            rule.start()
16741                .expect("rule should have a start token")
16742                .token_type(),
16743            1
16744        );
16745    }
16746
16747    #[test]
16748    fn parser_action_after_eof_stops_at_eof_token() {
16749        let atn = eof_then_action_atn();
16750        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16751
16752        let (_, actions) = parser
16753            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16754            .expect("EOF action rule should parse");
16755
16756        assert_eq!(actions.len(), 1);
16757        assert_eq!(actions[0].stop_index(), Some(0));
16758        assert_eq!(
16759            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
16760            ""
16761        );
16762    }
16763
16764    #[test]
16765    fn after_action_stop_uses_rule_context_stop_not_cursor() {
16766        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
16767        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
16768        // but the rule did not consume it. The @after stop must follow the rule
16769        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
16770        let mut id = TestToken::new(1).with_text("x");
16771        id.set_token_index(0);
16772        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
16773        eof.set_token_index(1);
16774        let mut parser = mini_parser(vec![id.clone(), eof]);
16775        // Advance the cursor onto EOF, as it would be after `a` matched ID.
16776        parser.consume();
16777        assert_eq!(parser.la(1), TOKEN_EOF);
16778
16779        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
16780        // exactly what finish_rule(consumed_eof = false) records.
16781        let mut ctx = ParserRuleContext::new(0, 0);
16782        parser.set_context_stop(
16783            &mut ctx,
16784            parser.token_id_at(0).expect("ID token should be buffered"),
16785        );
16786        let tree = parser.rule_node(ctx);
16787
16788        let current_index = parser.input.index();
16789        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
16790        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
16791        // ...but the tree-aware helper follows the rule context stop (ID).
16792        assert_eq!(
16793            parser.after_action_stop_index_for_tree(tree, current_index),
16794            Some(0)
16795        );
16796    }
16797
16798    #[test]
16799    fn after_action_start_uses_rule_context_start_not_cursor() {
16800        // A rule that begins after leading hidden-channel tokens: the rule context
16801        // start (set by `enter_rule`) is the first visible token, not the raw cursor
16802        // that may still point at the hidden prefix. The @after start must follow
16803        // the context start so `$start`/`$text` excludes the hidden prefix.
16804        let mut parser = mini_parser(vec![
16805            TestToken::new(9)
16806                .with_text(" ")
16807                .with_channel(HIDDEN_CHANNEL),
16808            TestToken::new(9)
16809                .with_text(" ")
16810                .with_channel(HIDDEN_CHANNEL),
16811            TestToken::new(1).with_text("x"),
16812            TestToken::eof("parser-test", 3, 1, 3),
16813        ]);
16814
16815        let mut ctx = ParserRuleContext::new(0, 0);
16816        parser.set_context_start(
16817            &mut ctx,
16818            parser.token_id_at(2).expect("ID token should be buffered"),
16819        );
16820        let tree = parser.rule_node(ctx);
16821
16822        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
16823        // ...but the tree-aware helper follows the rule context start (index 2).
16824        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
16825
16826        // With no rule start recorded, it falls back to the provided index.
16827        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
16828        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
16829    }
16830
16831    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
16832        FastRecognizeOutcome {
16833            index,
16834            consumed_eof,
16835            diagnostics: DiagnosticSeqId::EMPTY,
16836            deferred_nodes: FastDeferredNodeId::EMPTY,
16837            nodes: NodeSeqId(marker),
16838        }
16839    }
16840
16841    #[test]
16842    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
16843        let mut outcomes = vec![
16844            clean_fast_outcome(4, false, 0),
16845            clean_fast_outcome(2, false, 1),
16846            clean_fast_outcome(4, false, 2),
16847            clean_fast_outcome(4, true, 3),
16848            clean_fast_outcome(2, false, 4),
16849        ];
16850        let mut scratch = FastOutcomeDedupScratch::default();
16851
16852        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16853
16854        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
16855        assert_eq!(
16856            outcomes
16857                .iter()
16858                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
16859                .collect::<Vec<_>>(),
16860            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
16861        );
16862        assert!(scratch.dense_words.is_empty());
16863        assert!(scratch.sparse_keys.is_empty());
16864    }
16865
16866    #[test]
16867    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
16868        let mut scratch = FastOutcomeDedupScratch::default();
16869        let mut outcomes = (100..109)
16870            .flat_map(|index| {
16871                [
16872                    clean_fast_outcome(
16873                        index,
16874                        false,
16875                        u32::try_from(index).expect("test index fits in u32"),
16876                    ),
16877                    clean_fast_outcome(index, false, u32::MAX),
16878                ]
16879            })
16880            .collect();
16881
16882        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16883
16884        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16885        assert_eq!(outcomes.len(), 9);
16886        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
16887        let dense_capacity = scratch.dense_words.capacity();
16888
16889        let mut reused = (1_000..1_009)
16890            .map(|index| {
16891                clean_fast_outcome(
16892                    index,
16893                    false,
16894                    u32::try_from(index).expect("test index fits in u32"),
16895                )
16896            })
16897            .collect();
16898        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16899
16900        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16901        assert_eq!(reused.len(), 9);
16902        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
16903    }
16904
16905    #[test]
16906    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
16907        let mut scratch = FastOutcomeDedupScratch::default();
16908        let sparse_indexes = [
16909            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
16910        ];
16911        let mut outcomes = sparse_indexes
16912            .into_iter()
16913            .chain([400_000])
16914            .enumerate()
16915            .map(|(marker, index)| {
16916                clean_fast_outcome(
16917                    index,
16918                    false,
16919                    u32::try_from(marker).expect("test marker fits in u32"),
16920                )
16921            })
16922            .collect();
16923
16924        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16925
16926        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16927        assert_eq!(outcomes.len(), sparse_indexes.len());
16928        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
16929        let sparse_capacity = scratch.sparse_keys.capacity();
16930
16931        let mut reused = sparse_indexes
16932            .into_iter()
16933            .map(|index| {
16934                clean_fast_outcome(
16935                    index,
16936                    false,
16937                    u32::try_from(index).expect("test index fits in u32"),
16938                )
16939            })
16940            .collect();
16941        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16942
16943        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16944        assert_eq!(reused.len(), sparse_indexes.len());
16945        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
16946    }
16947
16948    #[test]
16949    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
16950        let mut scratch = FastOutcomeDedupScratch::default();
16951        scratch
16952            .sparse_keys
16953            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
16954        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16955        let mut outcomes = (0..9)
16956            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
16957            .collect();
16958
16959        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16960
16961        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16962        assert!(scratch.sparse_keys.is_empty());
16963        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16964    }
16965
16966    #[test]
16967    fn fast_outcome_selection_respects_sll_tie_order() {
16968        let mut arena = RecognitionArena::default();
16969        let first = FastRecognizeOutcome {
16970            index: 1,
16971            consumed_eof: false,
16972            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16973                line: 1,
16974                column: 0,
16975                message: "mismatched input 'x'".to_owned(),
16976            }]),
16977            deferred_nodes: FastDeferredNodeId::EMPTY,
16978            nodes: NodeSeqId::EMPTY,
16979        };
16980        let second = FastRecognizeOutcome {
16981            index: first.index,
16982            consumed_eof: first.consumed_eof,
16983            diagnostics: DiagnosticSeqId::EMPTY,
16984            deferred_nodes: FastDeferredNodeId::EMPTY,
16985            nodes: NodeSeqId::EMPTY,
16986        };
16987
16988        let selected = select_best_fast_outcome(
16989            [first, second].into_iter(),
16990            PredictionMode::Sll,
16991            None,
16992            |_| panic!("caller-follow token probe should not run"),
16993            &arena,
16994        )
16995        .expect("one outcome should be selected");
16996        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
16997        let eof_second = FastRecognizeOutcome {
16998            index: second.index,
16999            consumed_eof: true,
17000            diagnostics: DiagnosticSeqId::EMPTY,
17001            deferred_nodes: FastDeferredNodeId::EMPTY,
17002            nodes: NodeSeqId::EMPTY,
17003        };
17004        let selected = select_best_fast_outcome(
17005            [first, eof_second].into_iter(),
17006            PredictionMode::Sll,
17007            None,
17008            |_| panic!("caller-follow token probe should not run"),
17009            &arena,
17010        )
17011        .expect("one outcome should be selected");
17012        assert!(!selected.consumed_eof);
17013        let selected = select_best_fast_outcome(
17014            [first, second].into_iter(),
17015            PredictionMode::Ll,
17016            None,
17017            |_| panic!("caller-follow token probe should not run"),
17018            &arena,
17019        )
17020        .expect("one outcome should be selected");
17021        assert!(selected.diagnostics.is_empty());
17022    }
17023
17024    #[test]
17025    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
17026        let mut arena = RecognitionArena::default();
17027        let first = FastRecognizeOutcome {
17028            index: 3,
17029            consumed_eof: false,
17030            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17031                line: 1,
17032                column: 0,
17033                message: "mismatched input 'x' expecting 'a'".to_owned(),
17034            }]),
17035            deferred_nodes: FastDeferredNodeId::EMPTY,
17036            nodes: NodeSeqId::EMPTY,
17037        };
17038        let same_rank = FastRecognizeOutcome {
17039            index: first.index,
17040            consumed_eof: first.consumed_eof,
17041            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17042                line: 1,
17043                column: 0,
17044                message: "mismatched input 'x' expecting 'b'".to_owned(),
17045            }]),
17046            deferred_nodes: FastDeferredNodeId::EMPTY,
17047            nodes: NodeSeqId::EMPTY,
17048        };
17049        let better_rank = FastRecognizeOutcome {
17050            index: first.index,
17051            consumed_eof: first.consumed_eof,
17052            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
17053                line: 1,
17054                column: 0,
17055                message: "missing 'a' at 'x'".to_owned(),
17056            }]),
17057            deferred_nodes: FastDeferredNodeId::EMPTY,
17058            nodes: NodeSeqId::EMPTY,
17059        };
17060        let mut outcomes = vec![first, same_rank, better_rank];
17061
17062        dedupe_fast_outcomes(&mut outcomes, &arena);
17063
17064        assert_eq!(outcomes.len(), 2);
17065        assert_eq!(
17066            arena
17067                .diagnostics(outcomes[0].diagnostics)
17068                .next()
17069                .expect("first diagnostic")
17070                .message,
17071            "mismatched input 'x' expecting 'a'"
17072        );
17073        assert_eq!(
17074            arena
17075                .diagnostics(outcomes[1].diagnostics)
17076                .next()
17077                .expect("second diagnostic")
17078                .message,
17079            "missing 'a' at 'x'"
17080        );
17081    }
17082
17083    #[test]
17084    fn fast_outcome_selection_prefers_generated_caller_follow() {
17085        let arena = RecognitionArena::default();
17086        let earlier = FastRecognizeOutcome {
17087            index: 7,
17088            consumed_eof: false,
17089            diagnostics: DiagnosticSeqId::EMPTY,
17090            deferred_nodes: FastDeferredNodeId::EMPTY,
17091            nodes: NodeSeqId::EMPTY,
17092        };
17093        let later = FastRecognizeOutcome {
17094            index: 8,
17095            consumed_eof: false,
17096            diagnostics: DiagnosticSeqId::EMPTY,
17097            deferred_nodes: FastDeferredNodeId::EMPTY,
17098            nodes: NodeSeqId::EMPTY,
17099        };
17100        let mut follow = TokenBitSet::default();
17101        follow.insert(5);
17102
17103        let selected = select_best_fast_outcome(
17104            [later, earlier].into_iter(),
17105            PredictionMode::Ll,
17106            Some(&follow),
17107            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
17108            &arena,
17109        )
17110        .expect("one outcome should be selected");
17111        assert_eq!(selected.index, 7);
17112
17113        let selected = select_best_fast_outcome(
17114            [later, earlier].into_iter(),
17115            PredictionMode::Ll,
17116            Some(&follow),
17117            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
17118            &arena,
17119        )
17120        .expect("one outcome should be selected");
17121        assert_eq!(selected.index, 8);
17122
17123        let indented_next_statement = FastRecognizeOutcome {
17124            index: 9,
17125            consumed_eof: false,
17126            diagnostics: DiagnosticSeqId::EMPTY,
17127            deferred_nodes: FastDeferredNodeId::EMPTY,
17128            nodes: NodeSeqId::EMPTY,
17129        };
17130        let selected = select_best_fast_outcome(
17131            [indented_next_statement, earlier].into_iter(),
17132            PredictionMode::Ll,
17133            Some(&follow),
17134            |index| {
17135                let is_boundary = index == 7;
17136                let is_boundary_gap = matches!(index, 7 | 8);
17137                (
17138                    if index == 7 { 5 } else { TOKEN_EOF },
17139                    is_boundary,
17140                    is_boundary_gap,
17141                )
17142            },
17143            &arena,
17144        )
17145        .expect("one outcome should be selected");
17146        assert_eq!(selected.index, 7);
17147
17148        let continuation = FastRecognizeOutcome {
17149            index: 10,
17150            consumed_eof: false,
17151            diagnostics: DiagnosticSeqId::EMPTY,
17152            deferred_nodes: FastDeferredNodeId::EMPTY,
17153            nodes: NodeSeqId::EMPTY,
17154        };
17155        let selected = select_best_fast_outcome(
17156            [continuation, earlier].into_iter(),
17157            PredictionMode::Ll,
17158            Some(&follow),
17159            |index| {
17160                let is_boundary = matches!(index, 7 | 9);
17161                (
17162                    if index == 7 { 5 } else { TOKEN_EOF },
17163                    is_boundary,
17164                    is_boundary,
17165                )
17166            },
17167            &arena,
17168        )
17169        .expect("one outcome should be selected");
17170        assert_eq!(selected.index, 10);
17171
17172        let selected = select_best_fast_outcome(
17173            [earlier, later].into_iter(),
17174            PredictionMode::Sll,
17175            Some(&follow),
17176            |_| panic!("caller-follow token probe should not run in SLL mode"),
17177            &arena,
17178        )
17179        .expect("one outcome should be selected");
17180        assert_eq!(selected.index, 8);
17181    }
17182
17183    #[test]
17184    fn caller_follow_boundary_text_requires_separator_shape() {
17185        assert!(is_caller_follow_boundary_text(";"));
17186        assert!(is_caller_follow_boundary_text("\n"));
17187        assert!(is_caller_follow_boundary_text("\r\n  "));
17188        assert!(is_caller_follow_boundary_text(";\n"));
17189        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
17190        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
17191        assert!(!is_caller_follow_boundary_text("identifier"));
17192        assert!(is_caller_follow_boundary_gap_text(" \t "));
17193        assert!(is_caller_follow_boundary_gap_text("\n  "));
17194        assert!(is_caller_follow_boundary_gap_text(";\t"));
17195        assert!(!is_caller_follow_boundary_gap_text(
17196            "\"\"\"line1\nline2\"\"\""
17197        ));
17198        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
17199    }
17200
17201    #[test]
17202    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
17203        let mut parser = mini_parser(vec![
17204            TestToken::new(5).with_text("\n"),
17205            TestToken::new(6)
17206                .with_text("// comment\n")
17207                .with_channel(HIDDEN_CHANNEL),
17208            TestToken::new(1).with_text("x"),
17209            TestToken::eof("parser-test", 1, 2, 0),
17210        ]);
17211
17212        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17213        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
17214        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
17215    }
17216
17217    #[test]
17218    fn caller_follow_token_info_uses_stream_visible_channel() {
17219        let source = Source {
17220            tokens: vec![
17221                TestToken::new(5).with_text("\n").with_channel(2),
17222                TestToken::new(1).with_text("x").with_channel(2),
17223                TestToken::new(6)
17224                    .with_text("// comment\n")
17225                    .with_channel(HIDDEN_CHANNEL),
17226                TestToken::eof("parser-test", 1, 2, 0),
17227            ],
17228            index: 0,
17229        };
17230        let data = RecognizerData::new(
17231            "Mini.g4",
17232            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17233        );
17234        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
17235
17236        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17237        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
17238        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
17239    }
17240
17241    #[test]
17242    fn reset_per_parse_caches_clears_state_expected_token_cache() {
17243        let atn = token_then_eof_atn();
17244        let mut parser = mini_parser(Vec::new());
17245
17246        let _ = parser.cached_state_expected_token_set(&atn, 0);
17247        assert!(!parser.state_expected_token_cache.is_empty());
17248
17249        parser.reset_per_parse_caches();
17250        assert!(parser.state_expected_token_cache.is_empty());
17251    }
17252
17253    #[test]
17254    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
17255        let cyclic = epsilon_cycle_atn();
17256        let acyclic = token_then_eof_atn();
17257        let mut parser = mini_parser(Vec::new());
17258
17259        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17260        assert_eq!(
17261            parser.empty_cycle_cache_atn,
17262            Some(SharedAtnCacheKey::for_atn(&cyclic))
17263        );
17264        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17265
17266        parser.reset_per_parse_caches();
17267        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17268        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17269
17270        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
17271        assert_eq!(
17272            parser.empty_cycle_cache_atn,
17273            Some(SharedAtnCacheKey::for_atn(&acyclic))
17274        );
17275        assert_eq!(parser.empty_cycle_cache[1], Some(false));
17276    }
17277
17278    #[test]
17279    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
17280        let source = Source {
17281            tokens: vec![
17282                TestToken::new(1).with_text("x"),
17283                TestToken::eof("parser-test", 1, 1, 1),
17284            ],
17285            index: 0,
17286        };
17287        let data = RecognizerData::new(
17288            "Mini.g4",
17289            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17290        );
17291        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17292        let expected = ExpectedTokens {
17293            index: Some(0),
17294            symbols: BTreeSet::new(),
17295            no_viable: None,
17296        };
17297
17298        let (_, message) = parser.expected_error_message(0, 0, &expected);
17299
17300        assert_eq!(message, "mismatched input 'x'");
17301    }
17302
17303    #[test]
17304    fn eof_rule_stop_index_points_at_eof_token() {
17305        let source = Source {
17306            tokens: vec![
17307                TestToken::new(1).with_text("x"),
17308                TestToken::eof("parser-test", 1, 1, 1),
17309            ],
17310            index: 0,
17311        };
17312        let data = RecognizerData::new(
17313            "Mini.g4",
17314            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17315        );
17316        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17317
17318        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
17319        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
17320    }
17321
17322    #[test]
17323    fn generated_parser_action_uses_current_rule_stop_boundary() {
17324        let mut parser = mini_parser(vec![
17325            TestToken::new(1).with_text("x"),
17326            TestToken::eof("parser-test", 1, 1, 1),
17327        ]);
17328
17329        parser.match_token(1).expect("token should match");
17330        let action = parser.parser_action_at_current(7, 0, 0, false);
17331        assert_eq!(action.source_state(), 7);
17332        assert_eq!(action.rule_index(), 0);
17333        assert_eq!(action.start_index(), 0);
17334        assert_eq!(action.stop_index(), Some(0));
17335
17336        parser.match_eof().expect("EOF should match");
17337        let action = parser.parser_action_at_current(8, 0, 0, true);
17338        assert_eq!(action.stop_index(), Some(1));
17339    }
17340
17341    #[test]
17342    fn folds_left_recursive_boundary_into_rule_node() {
17343        let mut arena = RecognitionArena::default();
17344        let first = arena.push_node(ArenaRecognizedNode::Token {
17345            token: TokenId::try_from(0).expect("test token ID"),
17346        });
17347        let boundary =
17348            arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: 1 });
17349        let second = arena.push_node(ArenaRecognizedNode::Token {
17350            token: TokenId::try_from(1).expect("test token ID"),
17351        });
17352        let mut nodes = NodeSeqId::EMPTY;
17353        for node in [first, boundary, second].into_iter().rev() {
17354            nodes = arena.prepend(nodes, node);
17355        }
17356
17357        let folded = arena.fold_left_recursive_boundaries(nodes);
17358        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
17359
17360        assert_eq!(folded_nodes.len(), 2);
17361        let ArenaRecognizedNode::Rule {
17362            rule_index,
17363            invoking_state,
17364            start_index,
17365            stop_index,
17366            children,
17367            ..
17368        } = arena.node(folded_nodes[0])
17369        else {
17370            panic!("first folded node should be a rule");
17371        };
17372        assert_eq!(rule_index, 1);
17373        assert_eq!(invoking_state, -1);
17374        assert_eq!(start_index, 0);
17375        assert_eq!(stop_index, Some(0));
17376        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
17377        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
17378
17379        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
17380        assert_eq!(
17381            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17382            (4, 3, 1)
17383        );
17384        assert_eq!(
17385            (stats.total_links, stats.live_links, stats.dead_links),
17386            (9, 3, 6)
17387        );
17388    }
17389
17390    #[test]
17391    fn recognition_arena_reports_live_dead_and_retained_capacity() {
17392        let mut arena = RecognitionArena::default();
17393        let token = arena.push_node(ArenaRecognizedNode::Token {
17394            token: TokenId::try_from(0).expect("test token ID"),
17395        });
17396        let extra = arena.push_extra(RecognitionExtra::MissingToken {
17397            token_type: 2,
17398            at_index: 1,
17399            text: "<missing X>".to_owned(),
17400        });
17401        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
17402        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
17403            token: TokenId::try_from(1).expect("test token ID"),
17404        });
17405        let mut live = NodeSeqId::EMPTY;
17406        live = arena.prepend(live, missing);
17407        live = arena.prepend(live, token);
17408        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
17409        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17410            line: 1,
17411            column: 0,
17412            message: "missing X".to_owned(),
17413        }]);
17414        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17415            line: 1,
17416            column: 1,
17417            message: "discarded".to_owned(),
17418        }]);
17419        let deferred_children = arena.deferred_fragment(live);
17420        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
17421            rule_index: 0,
17422            invoking_state: -1,
17423            start_index: 0,
17424            stop_index: Some(1),
17425            deferred_children,
17426            children: NodeSeqId::EMPTY,
17427        });
17428
17429        let stats = arena.stats(live, live_diagnostics);
17430
17431        assert_eq!(
17432            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17433            (3, 2, 1)
17434        );
17435        assert_eq!(
17436            (stats.total_links, stats.live_links, stats.dead_links),
17437            (5, 3, 2)
17438        );
17439        assert_eq!(
17440            (stats.total_extras, stats.live_extras, stats.dead_extras),
17441            (3, 2, 1)
17442        );
17443        assert!(size_of::<SeqLink>() <= 8);
17444        assert!(size_of::<DiagnosticLink>() <= 8);
17445        assert!(size_of::<FastDeferredNode>() <= 12);
17446        assert!(size_of::<FastDeferredRule>() <= 28);
17447        assert!(size_of::<FastRecognizeOutcome>() <= 24);
17448        let capacities = (
17449            stats.node_capacity,
17450            stats.link_capacity,
17451            stats.extra_capacity,
17452        );
17453        let deferred_capacities = (
17454            arena.deferred_nodes.capacity(),
17455            arena.deferred_rules.capacity(),
17456        );
17457
17458        arena.reset();
17459        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
17460        assert_eq!(
17461            (reset.total_nodes, reset.total_links, reset.total_extras),
17462            (0, 0, 0)
17463        );
17464        assert_eq!(
17465            (
17466                reset.node_capacity,
17467                reset.link_capacity,
17468                reset.extra_capacity,
17469            ),
17470            capacities
17471        );
17472        assert!(arena.deferred_nodes.is_empty());
17473        assert!(arena.deferred_rules.is_empty());
17474        assert_eq!(
17475            (
17476                arena.deferred_nodes.capacity(),
17477                arena.deferred_rules.capacity(),
17478            ),
17479            deferred_capacities
17480        );
17481    }
17482
17483    #[test]
17484    fn parser_computes_recognition_arena_stats_on_demand() {
17485        let mut parser = mini_parser(Vec::new());
17486        let live = parser
17487            .recognition_arena
17488            .push_node(ArenaRecognizedNode::Token {
17489                token: TokenId::try_from(0).expect("test token ID"),
17490            });
17491        let discarded = parser
17492            .recognition_arena
17493            .push_node(ArenaRecognizedNode::ErrorToken {
17494                token: TokenId::try_from(1).expect("test token ID"),
17495            });
17496        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
17497        let _discarded_root = parser
17498            .recognition_arena
17499            .prepend(NodeSeqId::EMPTY, discarded);
17500        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
17501
17502        let stats = parser.recognition_arena_stats();
17503
17504        assert_eq!(
17505            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17506            (2, 1, 1)
17507        );
17508        assert_eq!(
17509            (stats.total_links, stats.live_links, stats.dead_links),
17510            (2, 1, 1)
17511        );
17512    }
17513
17514    #[test]
17515    fn recognition_arena_drops_capacity_above_retention_limit() {
17516        let mut storage = Vec::<u8>::with_capacity(4);
17517        storage.extend([1, 2, 3]);
17518
17519        reset_arena_vec(&mut storage, 3);
17520
17521        assert!(storage.is_empty());
17522        assert_eq!(storage.capacity(), 0);
17523    }
17524
17525    #[test]
17526    fn recognition_arena_concatenates_diagnostics_in_source_order() {
17527        let mut arena = RecognitionArena::default();
17528        let prefix = arena.diagnostic_sequence([
17529            ParserDiagnostic {
17530                line: 1,
17531                column: 0,
17532                message: "first".to_owned(),
17533            },
17534            ParserDiagnostic {
17535                line: 1,
17536                column: 1,
17537                message: "second".to_owned(),
17538            },
17539        ]);
17540        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
17541            line: 1,
17542            column: 2,
17543            message: "third".to_owned(),
17544        }]);
17545        let extras_before = arena.extras.len();
17546
17547        let combined = arena.concat_diagnostics(prefix, suffix);
17548        let messages = arena
17549            .diagnostics(combined)
17550            .map(|diagnostic| diagnostic.message.as_str())
17551            .collect::<Vec<_>>();
17552
17553        assert_eq!(messages, ["first", "second", "third"]);
17554        assert_eq!(arena.extras.len(), extras_before);
17555    }
17556
17557    #[test]
17558    fn outcome_ties_keep_later_non_recursive_alternative() {
17559        let arena = RecognitionArena::default();
17560        let first = RecognizeOutcome {
17561            index: 1,
17562            consumed_eof: false,
17563            alt_number: 0,
17564            member_values: BTreeMap::new(),
17565            return_values: BTreeMap::new(),
17566            diagnostics: DiagnosticSeqId::EMPTY,
17567            decisions: Vec::new(),
17568            actions: vec![ParserAction::new(1, 0, 0, None)],
17569            nodes: NodeSeqId::EMPTY,
17570        };
17571        let second = RecognizeOutcome {
17572            actions: vec![ParserAction::new(2, 0, 0, None)],
17573            ..first.clone()
17574        };
17575
17576        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17577            .expect("one outcome should be selected");
17578        assert_eq!(selected.actions[0].source_state(), 2);
17579    }
17580
17581    #[test]
17582    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
17583        let arena = RecognitionArena::default();
17584        let first = RecognizeOutcome {
17585            index: 1,
17586            consumed_eof: false,
17587            alt_number: 0,
17588            member_values: BTreeMap::new(),
17589            return_values: BTreeMap::new(),
17590            diagnostics: DiagnosticSeqId::EMPTY,
17591            decisions: Vec::new(),
17592            actions: vec![ParserAction::new(1, 0, 0, None)],
17593            nodes: NodeSeqId::EMPTY,
17594        };
17595        let second = RecognizeOutcome {
17596            actions: vec![
17597                ParserAction::new(2, 0, 0, None),
17598                ParserAction::new(3, 0, 0, None),
17599            ],
17600            ..first.clone()
17601        };
17602
17603        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
17604            .expect("one outcome should be selected");
17605        assert_eq!(selected.actions.len(), 2);
17606    }
17607
17608    #[test]
17609    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
17610        let arena = RecognitionArena::default();
17611        let first = RecognizeOutcome {
17612            index: 7,
17613            consumed_eof: false,
17614            alt_number: 0,
17615            member_values: BTreeMap::new(),
17616            return_values: BTreeMap::new(),
17617            diagnostics: DiagnosticSeqId::EMPTY,
17618            decisions: vec![1, 0],
17619            actions: vec![
17620                ParserAction::new(23, 2, 2, Some(4)),
17621                ParserAction::new(23, 2, 0, Some(6)),
17622            ],
17623            nodes: NodeSeqId::EMPTY,
17624        };
17625        let second = RecognizeOutcome {
17626            decisions: vec![0, 1],
17627            actions: vec![
17628                ParserAction::new(23, 2, 2, Some(6)),
17629                ParserAction::new(23, 2, 0, Some(6)),
17630            ],
17631            ..first.clone()
17632        };
17633
17634        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17635            .expect("one outcome should be selected");
17636        assert_eq!(selected.actions[0].stop_index(), Some(6));
17637    }
17638
17639    #[test]
17640    fn outcome_ties_keep_first_recursive_tree_shape() {
17641        let mut arena = RecognitionArena::default();
17642        let token = arena.push_node(ArenaRecognizedNode::Token {
17643            token: TokenId::try_from(0).expect("test token ID"),
17644        });
17645        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
17646        let inner = arena.push_node(ArenaRecognizedNode::Rule {
17647            rule_index: 1,
17648            invoking_state: -1,
17649            alt_number: 0,
17650            start_index: 0,
17651            stop_index: Some(0),
17652            return_values: None,
17653            children: token_children,
17654        });
17655        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
17656        let outer = arena.push_node(ArenaRecognizedNode::Rule {
17657            rule_index: 1,
17658            invoking_state: -1,
17659            alt_number: 0,
17660            start_index: 0,
17661            stop_index: Some(0),
17662            return_values: None,
17663            children: inner_children,
17664        });
17665        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
17666        let first = RecognizeOutcome {
17667            index: 1,
17668            consumed_eof: false,
17669            alt_number: 0,
17670            member_values: BTreeMap::new(),
17671            return_values: BTreeMap::new(),
17672            diagnostics: DiagnosticSeqId::EMPTY,
17673            decisions: Vec::new(),
17674            actions: vec![ParserAction::new(1, 0, 0, None)],
17675            nodes: recursive_nodes,
17676        };
17677        let second = RecognizeOutcome {
17678            index: 1,
17679            consumed_eof: false,
17680            alt_number: 0,
17681            member_values: BTreeMap::new(),
17682            return_values: BTreeMap::new(),
17683            diagnostics: DiagnosticSeqId::EMPTY,
17684            decisions: Vec::new(),
17685            actions: vec![ParserAction::new(2, 0, 0, None)],
17686            nodes: recursive_nodes,
17687        };
17688
17689        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17690            .expect("one outcome should be selected");
17691        assert_eq!(selected.actions[0].source_state(), 1);
17692    }
17693
17694    #[test]
17695    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
17696        let mut arena = RecognitionArena::default();
17697        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17698            line: 1,
17699            column: 3,
17700            message: "missing 'Y' at '<EOF>'".to_owned(),
17701        }]);
17702        let first_alt = RecognizeOutcome {
17703            index: 2,
17704            consumed_eof: true,
17705            alt_number: 0,
17706            member_values: BTreeMap::new(),
17707            return_values: BTreeMap::new(),
17708            diagnostics: recovered_diagnostics,
17709            decisions: vec![0],
17710            actions: vec![ParserAction::new(1, 0, 0, None)],
17711            nodes: NodeSeqId::EMPTY,
17712        };
17713        let second_alt = RecognizeOutcome {
17714            diagnostics: DiagnosticSeqId::EMPTY,
17715            decisions: vec![1],
17716            actions: vec![ParserAction::new(2, 0, 0, None)],
17717            ..first_alt.clone()
17718        };
17719
17720        let selected = select_best_outcome(
17721            [second_alt, first_alt].into_iter(),
17722            PredictionMode::Sll,
17723            &arena,
17724        )
17725        .expect("one outcome should be selected");
17726        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
17727        assert_eq!(selected.decisions, [0]);
17728    }
17729}