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/// Whole-rule direct adaptive execution is allowed to give up and fall back to
107/// the existing recognizer. Keep the guard at the same order of magnitude as
108/// speculative recognition so malformed cyclic ATNs cannot spin forever.
109const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
110/// Probe window for deciding whether clean-pass memo entries are reusable
111/// enough to keep caching. High-cardinality parses mostly produce one-shot
112/// entries; compact ambiguous loops repeatedly hit the same keys.
113const CLEAN_MEMO_PROBE_LIMIT: usize = 4096;
114const CLEAN_MEMO_REPEAT_LIMIT: usize = 8;
115/// Sparse parses periodically reopen the bounded probe so a repeat-heavy
116/// region that starts later in the token stream can promote memoization.
117const CLEAN_MEMO_REPROBE_INTERVAL: usize = 262_144;
118const FAST_RECOGNIZE_VISITING_CAPACITY: usize = 256;
119const FAST_RECOGNIZE_MIN_MEMO_CAPACITY: usize = 256;
120const FAST_RECOGNIZE_MAX_MEMO_CAPACITY: usize = 524_288;
121const FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY: usize = 65_536;
122
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124enum CleanMemoMode {
125    Probe,
126    Promote,
127    Sparse,
128}
129
130fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
131    intervals
132        .iter()
133        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
134}
135
136fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
137    let mut symbols = BTreeSet::new();
138    for (start, stop) in intervals {
139        symbols.extend(*start..=*stop);
140    }
141    symbols
142}
143
144fn interval_complement_symbols(
145    intervals: &[(i32, i32)],
146    min_vocabulary: i32,
147    max_vocabulary: i32,
148) -> BTreeSet<i32> {
149    (min_vocabulary..=max_vocabulary)
150        .filter(|symbol| !interval_set_contains(intervals, *symbol))
151        .collect()
152}
153
154#[cfg(feature = "perf-counters")]
155mod perf_counters {
156    use std::cell::Cell;
157    thread_local! {
158        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
159        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
160        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
161        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
162        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
163        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
164        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
165        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
166        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
167        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
168        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
169        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
170        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
171    }
172    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
173        c.with(|v| v.set(v.get() + n));
174    }
175    thread_local! {
176        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
177        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
178        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
179        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
180        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
181        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
182        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
183        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
184        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
185        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
186        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
187    }
188    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
189        [
190            ("rfs_calls", RFS_CALLS.with(Cell::get)),
191            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
192            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
193            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
194            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
195            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
196            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
197            (
198                "outcome_dedupe_inputs",
199                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
200            ),
201            (
202                "outcome_dedupe_removed",
203                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
204            ),
205            (
206                "outcome_dedupe_inline",
207                OUTCOME_DEDUPE_INLINE.with(Cell::get),
208            ),
209            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
210            (
211                "outcome_dedupe_sparse",
212                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
213            ),
214            (
215                "outcome_dedupe_dense_words",
216                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
217            ),
218            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
219            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
220            (
221                "atom_range_transitions",
222                ATOM_RANGE_TRANSITIONS.with(Cell::get),
223            ),
224            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
225            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
226            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
227            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
228            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
229            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
230            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
231            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
232        ]
233    }
234    pub fn reset() {
235        RFS_CALLS.with(|c| c.set(0));
236        RFS_MEMO_HITS.with(|c| c.set(0));
237        RFS_MEMO_MISSES.with(|c| c.set(0));
238        RFS_VISITING_CYCLE.with(|c| c.set(0));
239        MEMO_INSERTED.with(|c| c.set(0));
240        OUTCOMES_PUSHED.with(|c| c.set(0));
241        OUTCOMES_CLONED.with(|c| c.set(0));
242        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
243        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
244        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
245        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
246        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
247        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
248        EPSILON_TRANSITIONS.with(|c| c.set(0));
249        RULE_TRANSITIONS.with(|c| c.set(0));
250        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
251        SINGLE_TRANS_BODY.with(|c| c.set(0));
252        MULTI_TRANS_BODY.with(|c| c.set(0));
253        SINGLE_TRANS_RULE.with(|c| c.set(0));
254        SINGLE_TRANS_ATOM.with(|c| c.set(0));
255        SINGLE_TRANS_OTHER.with(|c| c.set(0));
256        OUTCOMES_RETURN_0.with(|c| c.set(0));
257        OUTCOMES_RETURN_1.with(|c| c.set(0));
258        OUTCOMES_RETURN_N.with(|c| c.set(0));
259    }
260    pub fn dump() {
261        for (name, value) in snapshot() {
262            #[allow(clippy::print_stderr)]
263            {
264                eprintln!("perf {name}={value}");
265            }
266        }
267    }
268}
269
270#[cfg(feature = "perf-counters")]
271pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
272/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
273/// fast recognizer has probed far enough that per-token stream sync dominates.
274/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
275/// switching long inputs to the cheaper filled-stream path before large fanout.
276const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
277/// Parser semantic action reached while recognizing one ATN path.
278///
279/// Generated parsers use `source_state` to dispatch back to the grammar action
280/// rendered for that ATN action transition. The token interval is the current
281/// rule's input span at the action site, which covers common target templates
282/// such as `$text`. Rule-init actions do not have an ATN action source state,
283/// so they are marked separately and may carry an ATN state for expected-token
284/// rendering.
285#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
286pub struct ParserAction {
287    source_state: usize,
288    rule_index: usize,
289    start_index: usize,
290    stop_index: Option<usize>,
291    rule_init: bool,
292    expected_state: Option<usize>,
293}
294
295impl ParserAction {
296    /// Creates an action event for a recognized parser path.
297    pub const fn new(
298        source_state: usize,
299        rule_index: usize,
300        start_index: usize,
301        stop_index: Option<usize>,
302    ) -> Self {
303        Self {
304            source_state,
305            rule_index,
306            start_index,
307            stop_index,
308            rule_init: false,
309            expected_state: None,
310        }
311    }
312
313    /// Creates an action event for a rule-level `@init` action.
314    pub const fn new_rule_init(
315        rule_index: usize,
316        start_index: usize,
317        expected_state: Option<usize>,
318    ) -> Self {
319        Self {
320            source_state: usize::MAX,
321            rule_index,
322            start_index,
323            stop_index: None,
324            rule_init: true,
325            expected_state,
326        }
327    }
328
329    /// ATN state that owns the semantic-action transition.
330    pub const fn source_state(&self) -> usize {
331        self.source_state
332    }
333
334    /// Grammar rule index recorded by the serialized ATN action transition.
335    pub const fn rule_index(&self) -> usize {
336        self.rule_index
337    }
338
339    /// Token-stream index where the active rule began.
340    pub const fn start_index(&self) -> usize {
341        self.start_index
342    }
343
344    /// Last token-stream index consumed before the action was reached.
345    pub const fn stop_index(&self) -> Option<usize> {
346        self.stop_index
347    }
348
349    /// Reports whether this event represents a rule-level `@init` action.
350    pub const fn is_rule_init(&self) -> bool {
351        self.rule_init
352    }
353
354    /// ATN state used to compute expected-token display for this action.
355    pub const fn expected_state(&self) -> Option<usize> {
356        self.expected_state
357    }
358}
359
360/// Runtime view passed to parser semantic hooks.
361///
362/// The context is intentionally read-only with respect to parser structure:
363/// predicates may run speculatively during prediction, and hooks can be called
364/// more than once for paths that are later abandoned. Lookahead methods may
365/// buffer tokens from the underlying token source, matching normal parser
366/// prediction behavior.
367pub struct ParserSemCtx<'a, S>
368where
369    S: TokenSource,
370{
371    input: &'a mut CommonTokenStream<S>,
372    tree_storage: &'a ParseTreeStorage,
373    rule_index: usize,
374    coordinate_index: usize,
375    rule_name: Option<String>,
376    context: Option<&'a ParserRuleContext>,
377    tree: Option<ParseTree>,
378    local_int_arg: Option<(usize, i64)>,
379    member_values: &'a BTreeMap<usize, i64>,
380    action: Option<ParserAction>,
381}
382
383impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
384where
385    S: TokenSource,
386{
387    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388        f.debug_struct("ParserSemCtx")
389            .field("rule_index", &self.rule_index)
390            .field("coordinate_index", &self.coordinate_index)
391            .field("rule_name", &self.rule_name)
392            .field("context", &self.context)
393            .field("tree", &self.tree)
394            .field("local_int_arg", &self.local_int_arg)
395            .field("member_values", &self.member_values)
396            .field("action", &self.action)
397            .finish_non_exhaustive()
398    }
399}
400
401impl<'a, S> ParserSemCtx<'a, S>
402where
403    S: TokenSource,
404{
405    /// Rule index that owns the predicate/action coordinate.
406    #[must_use]
407    pub const fn rule_index(&self) -> usize {
408        self.rule_index
409    }
410
411    /// Rule name that owns the coordinate, when recognizer metadata has it.
412    #[must_use]
413    pub fn rule_name(&self) -> Option<&str> {
414        self.rule_name.as_deref()
415    }
416
417    /// Predicate/action index inside the owning rule. Parser actions keyed only
418    /// by ATN source state report `usize::MAX` here; use [`Self::action`] for
419    /// the stable action event.
420    #[must_use]
421    pub const fn coordinate_index(&self) -> usize {
422        self.coordinate_index
423    }
424
425    /// Current token-stream index.
426    #[must_use]
427    pub fn input_index(&self) -> usize {
428        self.input.index()
429    }
430
431    /// Token type at one-based lookahead/lookbehind offset.
432    pub fn la(&mut self, offset: isize) -> i32 {
433        self.input.la(offset)
434    }
435
436    /// Token at one-based lookahead/lookbehind offset.
437    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
438        self.input.lt(offset)
439    }
440
441    /// Borrowing token view for text inspection at a one-based offset.
442    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
443        self.lt(offset)
444    }
445
446    /// Token at an absolute buffered index, including hidden/custom channels.
447    ///
448    /// Unlike [`Self::lt`], this does not apply the token stream's channel
449    /// filter and does not move its cursor. It is intended for semantic helpers
450    /// such as automatic-semicolon-insertion checks that inspect trivia
451    /// immediately before the current visible token.
452    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
453        self.input.get(index)
454    }
455
456    /// Current generated rule context, when a generated rule predicate supplied
457    /// one.
458    #[must_use]
459    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
460        self.context
461    }
462
463    /// Flat tree storage containing completed children visible to this hook.
464    #[must_use]
465    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
466        self.tree_storage
467    }
468
469    /// Canonical token store used by completed flat-tree nodes.
470    #[must_use]
471    pub const fn token_store(&self) -> &TokenStore {
472        self.input.token_store()
473    }
474
475    /// Completed parse-tree root ID passed to a replayed action hook.
476    #[must_use]
477    pub const fn tree_id(&self) -> Option<NodeId> {
478        self.tree
479    }
480
481    /// Completed parse tree passed to an action hook, if the action is being
482    /// replayed after recognition.
483    #[must_use]
484    pub fn tree(&self) -> Option<Node<'_>> {
485        self.tree
486            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
487    }
488
489    /// Integer local argument visible to this predicate coordinate.
490    #[must_use]
491    pub fn local_int_arg(&self) -> Option<i64> {
492        self.local_int_arg.map(|(_, value)| value)
493    }
494
495    /// Integer member value observed on the current speculative path.
496    #[must_use]
497    pub fn member_int(&self, member: usize) -> Option<i64> {
498        self.member_values.get(&member).copied()
499    }
500
501    /// Parser action event being replayed, when this context belongs to an
502    /// action hook.
503    #[must_use]
504    pub const fn action(&self) -> Option<ParserAction> {
505        self.action
506    }
507
508    /// Text covered by a parser action event.
509    ///
510    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
511    /// EOF the interval ends at the previous *visible* token, so trailing hidden
512    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
513    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
514    /// itself guards `start > stop`, so an empty interval yields `""`.
515    pub fn action_text(&self) -> String {
516        let Some(action) = self.action else {
517            return String::new();
518        };
519        let Some(stop) = action.stop_index() else {
520            return String::new();
521        };
522        let stop = if self
523            .input
524            .get(stop)
525            .is_some_and(|token| token.token_type() == TOKEN_EOF)
526        {
527            let Some(previous) = self.input.previous_visible_token_index(stop) else {
528                return String::new();
529            };
530            previous
531        } else {
532            stop
533        };
534        self.input.text(action.start_index(), stop)
535    }
536}
537
538/// User extension point for parser semantic predicates and actions that the
539/// metadata generator did not translate into built-in runtime metadata.
540///
541/// Returning `None`/`false` says "not handled", so the runtime falls through
542/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
543/// speculative prediction and must be replay-safe.
544pub trait SemanticHooks {
545    /// Whether generated lexers should route lifecycle callbacks through this
546    /// hook object.
547    ///
548    /// User hook implementations opt in by default. [`NoSemanticHooks`]
549    /// overrides this to keep generated lexers on the direct no-extension
550    /// token path.
551    const ENABLES_LEXER_LIFECYCLE: bool = true;
552
553    /// Whether this hook object may observe parser predicate transitions.
554    ///
555    /// Custom hooks default to conservative predicate handling so the fast
556    /// recognizer does not bypass a `sempred` implementation.
557    fn observes_parser_predicates(&self) -> bool {
558        true
559    }
560
561    fn sempred<S>(
562        &mut self,
563        ctx: &mut ParserSemCtx<'_, S>,
564        rule_index: usize,
565        pred_index: usize,
566    ) -> Option<bool>
567    where
568        S: TokenSource,
569    {
570        let _ = (ctx, rule_index, pred_index);
571        None
572    }
573
574    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
575    where
576        S: TokenSource,
577    {
578        let _ = (ctx, action);
579        false
580    }
581
582    fn lexer_sempred<I>(
583        &mut self,
584        ctx: &mut LexerSemCtx<'_, I>,
585        rule_index: usize,
586        pred_index: usize,
587    ) -> Option<bool>
588    where
589        I: CharStream,
590    {
591        let _ = (ctx, rule_index, pred_index);
592        None
593    }
594
595    /// Runs a lexer custom action on the committed lexing path. Returns whether
596    /// the hook handled the action.
597    ///
598    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
599    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
600    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
601    /// queued prefix tokens, just like the closure-based `custom_action` API.
602    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
603    /// borrow, so those mutators are inert there.)
604    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
605    where
606        I: CharStream,
607    {
608        let _ = (ctx, action);
609        false
610    }
611
612    /// Runs after runtime-owned lexer state has been reset for reuse.
613    ///
614    /// Implementations should clear extension-owned transient state here.
615    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
616    where
617        I: CharStream,
618    {
619        let _ = ctx;
620    }
621
622    /// Runs before the runtime returns a queued token or starts a new ATN
623    /// token match.
624    ///
625    /// The callback also runs between internal `skip`/`more` matches, so it
626    /// observes every point where another ATN match may start.
627    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
628    where
629        I: CharStream,
630    {
631        let _ = ctx;
632    }
633
634    /// Runs after the accepted path's portable and custom actions, but before
635    /// the token span is finalized and emitted.
636    ///
637    /// Accepted paths that selected `skip` or `more` are included, and the hook
638    /// may observe or override that pending token type.
639    ///
640    /// This callback has no synthetic ATN coordinate. It therefore also runs
641    /// for accepted rules that contain no action or predicate.
642    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
643    where
644        I: CharStream,
645    {
646        let _ = ctx;
647    }
648
649    /// Observes a token after committed lexer actions and portable commands
650    /// have run and the token has been emitted, immediately before it is
651    /// returned to the token stream.
652    ///
653    /// Hidden and custom-channel tokens are included. `skip` and intermediate
654    /// `more` matches do not produce callbacks.
655    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
656        let _ = token;
657    }
658}
659
660/// Default hook object used by parsers that do not need user-supplied
661/// semantics.
662#[derive(Clone, Copy, Debug, Default)]
663pub struct NoSemanticHooks;
664
665impl SemanticHooks for NoSemanticHooks {
666    const ENABLES_LEXER_LIFECYCLE: bool = false;
667
668    fn observes_parser_predicates(&self) -> bool {
669        false
670    }
671}
672
673/// Parser semantic predicate rendered from a supported target template.
674///
675/// The metadata recognizer evaluates these at the token-stream index where the
676/// predicate transition is reached. Unsupported or absent predicate templates
677/// remain unconditional so existing generated parsers keep their previous
678/// behavior unless the generator opts into this table.
679#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
680pub enum ParserPredicate {
681    True,
682    False,
683    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
684    FalseWithMessage {
685        message: &'static str,
686    },
687    /// Target-template test helper that reports predicate evaluation before
688    /// returning the wrapped boolean value.
689    Invoke {
690        value: bool,
691    },
692    LookaheadTextEquals {
693        offset: isize,
694        text: &'static str,
695    },
696    LookaheadNotEquals {
697        offset: isize,
698        token_type: i32,
699    },
700    /// Checks that the last two consumed visible tokens were adjacent in the
701    /// token stream. Used by C# parser predicates for split operator tokens.
702    TokenPairAdjacent,
703    /// Checks a generated parser context child by rule index and text.
704    ///
705    /// If the child is absent the predicate succeeds, matching target helpers
706    /// that treat incomplete or non-matching contexts as non-restrictive.
707    ContextChildRuleTextNotEquals {
708        rule_index: usize,
709        text: &'static str,
710    },
711    /// Compares the current rule invocation's integer argument with a literal
712    /// value from a supported `ValEquals("$i", "...")` target template.
713    LocalIntEquals {
714        value: i64,
715    },
716    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
717    /// rule invocation's integer argument.
718    LocalIntLessOrEqual {
719        value: i64,
720    },
721    /// Compares a generated parser integer member modulo a literal value.
722    MemberModuloEquals {
723        member: usize,
724        modulus: i64,
725        value: i64,
726        equals: bool,
727    },
728    /// Compares a generated parser integer member with a literal value.
729    MemberEquals {
730        member: usize,
731        value: i64,
732        equals: bool,
733    },
734}
735
736impl ParserPredicate {
737    /// Lowers the legacy predicate metadata variant into `SemIR`.
738    ///
739    /// This is the compatibility adapter for generated parsers produced while
740    /// the runtime still emitted closed enum tables. Newer generated parsers
741    /// emit `SemIR` directly.
742    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
743        match self {
744            Self::True => ir.expr(PExpr::Bool(true)),
745            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
746            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
747            Self::LookaheadTextEquals { offset, text } => {
748                let token = ir.expr(PExpr::TokenText(offset));
749                let text = ir.intern(text);
750                let text = ir.expr(PExpr::Str(text));
751                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
752            }
753            Self::LookaheadNotEquals { offset, token_type } => {
754                let actual = ir.expr(PExpr::La(offset));
755                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
756                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
757            }
758            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
759            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
760                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
761                let expected = ir.intern(text);
762                let expected = ir.expr(PExpr::Str(expected));
763                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
764            }
765            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
766            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
767            Self::MemberModuloEquals {
768                member,
769                modulus,
770                value,
771                equals,
772            } => {
773                if modulus == 0 {
774                    return ir.expr(PExpr::Bool(false));
775                }
776                let member = ir.expr(PExpr::Member(member));
777                let modulus = ir.expr(PExpr::Int(modulus));
778                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
779                let expected = ir.expr(PExpr::Int(value));
780                ir.expr(PExpr::Cmp(
781                    if equals { CmpOp::Eq } else { CmpOp::Ne },
782                    actual,
783                    expected,
784                ))
785            }
786            Self::MemberEquals {
787                member,
788                value,
789                equals,
790            } => {
791                let actual = ir.expr(PExpr::Member(member));
792                let expected = ir.expr(PExpr::Int(value));
793                ir.expr(PExpr::Cmp(
794                    if equals { CmpOp::Eq } else { CmpOp::Ne },
795                    actual,
796                    expected,
797                ))
798            }
799        }
800    }
801
802    #[must_use]
803    pub const fn failure_message(self) -> Option<&'static str> {
804        match self {
805            Self::FalseWithMessage { message } => Some(message),
806            Self::True
807            | Self::False
808            | Self::Invoke { .. }
809            | Self::LookaheadTextEquals { .. }
810            | Self::LookaheadNotEquals { .. }
811            | Self::TokenPairAdjacent
812            | Self::ContextChildRuleTextNotEquals { .. }
813            | Self::LocalIntEquals { .. }
814            | Self::LocalIntLessOrEqual { .. }
815            | Self::MemberModuloEquals { .. }
816            | Self::MemberEquals { .. } => None,
817        }
818    }
819}
820
821fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
822    let local = ir.expr(PExpr::LocalArg);
823    let absent = ir.expr(PExpr::IsNull(local));
824    let expected = ir.expr(PExpr::Int(value));
825    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
826    ir.expr(PExpr::Or([absent, comparison].into()))
827}
828
829/// Policy for semantic predicate coordinates that have no runtime
830/// implementation.
831///
832/// ANTLR grammars may embed target-language predicates that the metadata
833/// generator could not translate into a [`ParserPredicate`] table entry. When
834/// recognition reaches such a coordinate the runtime cannot know the grammar
835/// author's intent, so the caller chooses how to proceed.
836///
837/// The default is [`Self::AssumeTrue`], matching the historical behavior of
838/// this runtime. That default is deprecated and will change to [`Self::Error`]
839/// in a future minor release; grammars relying on unconditional predicates
840/// should opt in explicitly.
841#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
842pub enum UnknownSemanticPolicy {
843    /// Treat the predicate as passing, as if it were absent from the grammar.
844    #[default]
845    AssumeTrue,
846    /// Treat the predicate as failing, removing the guarded alternative.
847    AssumeFalse,
848    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
849    /// coordinate that recognition evaluated.
850    Error,
851}
852
853/// Resolves a predicate coordinate that neither a translated table entry nor a
854/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
855///
856/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
857/// so the parse entry can surface every unresolved coordinate afterwards. Both
858/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
859/// funnel through here so a missing implementation is never silently coerced
860/// to a boolean (design goal G1: never silently mis-parse).
861fn apply_unknown_predicate_policy(
862    policy: UnknownSemanticPolicy,
863    rule_index: usize,
864    pred_index: usize,
865    hits: &mut Vec<(usize, usize)>,
866) -> bool {
867    match policy {
868        UnknownSemanticPolicy::AssumeTrue => true,
869        UnknownSemanticPolicy::AssumeFalse => false,
870        UnknownSemanticPolicy::Error => {
871            let coordinate = (rule_index, pred_index);
872            if !hits.contains(&coordinate) {
873                hits.push(coordinate);
874            }
875            false
876        }
877    }
878}
879
880/// Interval-set of expected token types, displayable through a vocabulary —
881/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
882/// generated test actions.
883#[derive(Clone, Debug, Eq, PartialEq)]
884pub struct ExpectedTokenSet {
885    symbols: BTreeSet<i32>,
886}
887
888impl ExpectedTokenSet {
889    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
890    #[must_use]
891    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
892        expected_symbols_display(&self.symbols, vocabulary)
893    }
894}
895
896/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
897///
898/// The first syntax error aborts the parse instead of recovering. Generated
899/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
900#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
901pub struct BailErrorStrategy;
902
903impl BailErrorStrategy {
904    #[must_use]
905    pub const fn new() -> Self {
906        Self
907    }
908}
909
910/// Prediction strategy requested by generated parser harnesses.
911#[derive(Clone, Copy, Debug, Eq, PartialEq)]
912pub enum PredictionMode {
913    /// Prefer the clean full-context outcome when alternatives reach the same
914    /// input position.
915    Ll,
916    /// Preserve SLL's first-viable alternative bias at a decision, even when a
917    /// later full-context alternative could avoid recovery.
918    Sll,
919    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
920    LlExactAmbigDetection,
921}
922
923/// Integer argument metadata for a generated parser rule invocation.
924///
925/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
926/// the generator records the rule-transition source state and the value that
927/// should be visible to semantic predicates inside the callee.
928#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
929pub struct ParserRuleArg {
930    /// ATN state containing the rule transition that receives this argument.
931    pub source_state: usize,
932    /// Callee rule index for the transition.
933    pub rule_index: usize,
934    /// Literal fallback value to expose in the callee.
935    pub value: i64,
936    /// Whether the callee should inherit the caller's current integer argument.
937    pub inherit_local: bool,
938}
939
940/// Integer member mutation attached to an ATN action transition.
941#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
942pub struct ParserMemberAction {
943    /// ATN state containing the action transition.
944    pub source_state: usize,
945    /// Generator-assigned integer member id.
946    pub member: usize,
947    /// Delta applied when the action is reached on one speculative path.
948    pub delta: i64,
949}
950
951/// Integer return-value assignment attached to an ATN action transition.
952///
953/// Generated parsers use this metadata when target actions assign a simple
954/// return field such as `$y=1000;`. The interpreter applies it while selecting
955/// the recognized path so the finished parse tree can answer later
956/// `$label.y` action templates.
957#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
958pub struct ParserReturnAction {
959    /// ATN state containing the action transition.
960    pub source_state: usize,
961    /// Rule index recorded by the serialized action transition.
962    pub rule_index: usize,
963    /// Return-field name as it appears in the grammar.
964    pub name: &'static str,
965    /// Literal integer value assigned by the action.
966    pub value: i64,
967}
968
969impl ParserMemberAction {
970    /// Lowers this speculative member mutation into a `SemIR` action.
971    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
972        let delta = ir.expr(PExpr::Int(self.delta));
973        ParserSemanticAction {
974            source_state: self.source_state,
975            rule_index: usize::MAX,
976            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
977            speculative: true,
978        }
979    }
980}
981
982impl ParserReturnAction {
983    /// Lowers this committed return-value assignment into a `SemIR` action.
984    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
985        let name = ir.intern(self.name);
986        let value = ir.expr(PExpr::Int(self.value));
987        ParserSemanticAction {
988            source_state: self.source_state,
989            rule_index: self.rule_index,
990            stmt: ir.stmt(AStmt::SetReturn(name, value)),
991            speculative: false,
992        }
993    }
994}
995
996/// Parser predicate coordinate lowered into [`SemIr`].
997#[derive(Clone, Copy, Debug, Eq, PartialEq)]
998pub struct ParserSemanticPredicate {
999    /// Serialized rule index that owns this predicate.
1000    pub rule_index: usize,
1001    /// Predicate index inside the owning rule.
1002    pub pred_index: usize,
1003    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1004    pub expr: ExprId,
1005    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1006    pub failure_message: Option<&'static str>,
1007}
1008
1009/// Parser action coordinate lowered into [`SemIr`].
1010#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1011pub struct ParserSemanticAction {
1012    /// ATN state containing the action transition.
1013    pub source_state: usize,
1014    /// Serialized rule index recorded by the action transition.
1015    pub rule_index: usize,
1016    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1017    pub stmt: StmtId,
1018    /// Whether this action may run on speculative recognition paths.
1019    pub speculative: bool,
1020}
1021
1022/// Data-driven semantic tables emitted by generated parsers.
1023///
1024/// This is the runtime representation for issue #9's `SemIR` path. Existing
1025/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1026/// remain accepted as deprecated adapters for generated code produced before
1027/// this table existed.
1028#[derive(Clone, Debug, Default, Eq, PartialEq)]
1029pub struct ParserSemantics {
1030    pub ir: SemIr,
1031    pub predicates: Vec<ParserSemanticPredicate>,
1032    pub actions: Vec<ParserSemanticAction>,
1033}
1034
1035/// Optional generated-runtime metadata for metadata-driven parser execution.
1036#[derive(Clone, Copy, Debug, Default)]
1037pub struct ParserRuntimeOptions<'a> {
1038    /// Rule indexes whose `@init` actions should be replayed.
1039    pub init_action_rules: &'a [usize],
1040    /// Whether generated parse-tree contexts should retain alternative numbers.
1041    pub track_alt_numbers: bool,
1042    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1043    pub predicates: &'a [(usize, usize, ParserPredicate)],
1044    /// `SemIR` predicate/action table emitted by newer generated parsers.
1045    pub semantics: Option<&'a ParserSemantics>,
1046    /// Rule-call integer argument table keyed by ATN source state.
1047    pub rule_args: &'a [ParserRuleArg],
1048    /// Integer member mutations keyed by ATN action source state.
1049    pub member_actions: &'a [ParserMemberAction],
1050    /// Integer return assignments keyed by ATN action source state.
1051    pub return_actions: &'a [ParserReturnAction],
1052    /// How to evaluate semantic predicate coordinates absent from
1053    /// `predicates`.
1054    pub unknown_predicate_policy: UnknownSemanticPolicy,
1055}
1056
1057pub trait Parser: Recognizer {
1058    /// Reports whether generated parser rules should build parse-tree nodes
1059    /// while recognizing input.
1060    fn build_parse_trees(&self) -> bool;
1061
1062    /// Enables or disables parse-tree construction for subsequent rule calls.
1063    fn set_build_parse_trees(&mut self, build: bool);
1064
1065    /// Returns the number of parser syntax errors recorded by committed parse
1066    /// paths so far.
1067    fn number_of_syntax_errors(&self) -> usize {
1068        0
1069    }
1070
1071    /// Reports whether prediction diagnostic-listener messages are emitted
1072    /// during parser ATN recognition.
1073    fn report_diagnostic_errors(&self) -> bool {
1074        false
1075    }
1076
1077    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1078    /// rule calls.
1079    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1080
1081    /// Reports the prediction strategy used when selecting among alternatives.
1082    fn prediction_mode(&self) -> PredictionMode {
1083        PredictionMode::Ll
1084    }
1085
1086    /// Sets the prediction strategy for subsequent rule calls.
1087    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1088}
1089
1090#[derive(Debug)]
1091struct LeftRecursiveCallerOverlap {
1092    atn_key: SharedAtnCacheKey,
1093    state_number: usize,
1094    symbol: i32,
1095    context_version: usize,
1096    overlaps: bool,
1097}
1098
1099const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1100
1101#[derive(Debug)]
1102pub struct BaseParser<S, H = NoSemanticHooks> {
1103    input: CommonTokenStream<S>,
1104    tree: ParseTreeStorage,
1105    data: RecognizerData,
1106    semantic_hooks: H,
1107    build_parse_trees: bool,
1108    syntax_errors: usize,
1109    report_diagnostic_errors: bool,
1110    prediction_mode: PredictionMode,
1111    prediction_diagnostics: Vec<ParserDiagnostic>,
1112    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1113    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1114    generated_sync_expected: Option<TokenBitSet>,
1115    int_members: BTreeMap<usize, i64>,
1116    rule_context_stack: Vec<RuleContextFrame>,
1117    rule_context_version: usize,
1118    left_recursive_caller_overlap_cache:
1119        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1120    pending_invoking_states: Vec<isize>,
1121    precedence_stack: Vec<i32>,
1122    /// Predicate side effects are observable in a few target-template tests;
1123    /// speculative recognition may revisit the same coordinate, so replay it
1124    /// once per parser instance.
1125    invoked_predicates: Vec<(usize, usize)>,
1126    /// Bail error strategy: the first syntax error aborts the parse instead of
1127    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1128    /// through `set_error_handler(BailErrorStrategy::new())`.
1129    bail_on_error: bool,
1130    /// How to evaluate predicate coordinates missing from the active
1131    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1132    unknown_predicate_policy: UnknownSemanticPolicy,
1133    /// Unknown predicate coordinates evaluated by the current parse, recorded
1134    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1135    unknown_predicate_hits: Vec<(usize, usize)>,
1136    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1137    /// that no hook handled, recorded so a generated `hook`/error-disposed
1138    /// action fails loud instead of being silently dropped. Keyed by
1139    /// `(rule_index, source_state)`.
1140    unhandled_action_hits: Vec<(usize, usize)>,
1141    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1142    /// hot rule-transition checks to a vector lookup after the first visit
1143    /// while the thread-local shared ATN cache still owns the cross-parse
1144    /// computed value.
1145    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1146    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1147    /// epsilon-reachable consuming transition and shows up as a hot loop in
1148    /// `next_recovery_context` and recovery diagnostics on long inputs.
1149    /// Keying on `state_number` and sharing the result through `Rc` removes
1150    /// repeated DFS plus per-call `BTreeSet` allocations.
1151    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1152    /// Same expected-symbol cache as a bitset for generated parser sync.
1153    /// Successful parses only need `contains` and union; keeping that path out
1154    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1155    /// check and defers deterministic formatting to diagnostics.
1156    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1157    /// Per-state cache for whether a return state can finish its owning rule
1158    /// without consuming more input. Generated-parser sync uses this to walk
1159    /// parent prediction contexts for nullable exits without paying repeated
1160    /// epsilon-closure searches on every loop or optional decision.
1161    rule_stop_reach_cache: Vec<Option<bool>>,
1162    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1163    /// threads the same epsilon-recovery context through hundreds of follow
1164    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1165    /// reference bump and lets the memo key hash by pointer.
1166    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1167    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1168    /// touch a given decision state still pay no upfront cost; once cached,
1169    /// the recognizer prunes alternatives whose look-1 cannot accept the
1170    /// current lookahead, letting common SLL decisions reduce to a single
1171    /// transition walk instead of a full speculative fan-out.
1172    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1173    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1174    /// Each multi-trans visit asks "given this decision state and this
1175    /// lookahead token, which alt do I commit to?" Hitting this cache
1176    /// turns the question into a hashmap probe instead of re-scanning
1177    /// the decision's per-transition FIRST sets every visit.
1178    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1179    /// Predicate results shared by the fast recognizer's clean and recovery
1180    /// attempts. The eligible fast path keeps every runtime-provided input
1181    /// fixed, and custom predicate hooks are required to be replay-safe.
1182    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1183    /// Cache for whether an ATN state can reach itself without consuming
1184    /// input. Only those states need the recursive recognizer's
1185    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1186    /// grammar-static cache survive parser resets without reusing state
1187    /// coordinates after the parser is driven against a different ATN.
1188    empty_cycle_cache: Vec<Option<bool>>,
1189    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1190    /// Probe state for deciding whether clean-pass memo entries are worth
1191    /// storing for the current parse.
1192    clean_memo_mode: CleanMemoMode,
1193    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1194    clean_memo_probe_samples: usize,
1195    clean_memo_probe_repeats: usize,
1196    clean_memo_sparse_samples: usize,
1197    /// Reusable cycle and memo storage for one top-level fast recognition.
1198    fast_recognize_scratch: FastRecognizeTopScratch,
1199    /// Reusable direct-index/hash storage for clean speculative endpoints.
1200    fast_outcome_dedup: FastOutcomeDedupScratch,
1201    /// Empty recovery-symbols singleton used as the default at rule entry and
1202    /// after token consumption.
1203    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1204    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1205    /// prefilter trims speculative rule calls whose called rule cannot
1206    /// match the current lookahead, but it also bypasses single-token
1207    /// insertion / deletion recovery that ANTLR runs at the rule's first
1208    /// consuming transition. `parse_atn_rule` flips this off and retries
1209    /// when the first pass produces no clean outcome so the runtime can
1210    /// repair inputs the reference parser would have repaired.
1211    fast_first_set_prefilter: bool,
1212    /// Whether the fast recognizer should explore parser error-recovery paths.
1213    /// Public rule parsing starts with this disabled for the common valid-input
1214    /// path and enables it only for the retry that needs ANTLR-style repairs.
1215    fast_recovery_enabled: bool,
1216    /// Whether the fast recognizer should record terminal-token nodes while
1217    /// speculating. Clean valid-input parsing can reconstruct terminals from
1218    /// selected rule spans after recognition, avoiding many speculative
1219    /// nodes that are thrown away with losing paths.
1220    fast_token_nodes_enabled: bool,
1221    /// Parser-owned append-only storage for speculative recognition output.
1222    /// Each public interpreted-rule entry clears lengths while retaining
1223    /// bounded backing capacities for parser reuse.
1224    recognition_arena: RecognitionArena,
1225    last_recognition_arena_root: NodeSeqId,
1226    last_recognition_arena_diagnostics: DiagnosticSeqId,
1227}
1228
1229/// Rollback marker for speculative generated parser paths.
1230#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1231pub struct GeneratedDiagnosticsCheckpoint {
1232    diagnostics_len: usize,
1233    syntax_errors: usize,
1234    tree: ParseTreeCheckpoint,
1235}
1236
1237/// Storage and reachability counters for the most recent interpreted-rule
1238/// recognition arena.
1239#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1240pub struct RecognitionArenaStats {
1241    pub total_nodes: usize,
1242    pub live_nodes: usize,
1243    pub dead_nodes: usize,
1244    pub node_capacity: usize,
1245    pub total_links: usize,
1246    pub live_links: usize,
1247    pub dead_links: usize,
1248    pub link_capacity: usize,
1249    pub total_extras: usize,
1250    pub live_extras: usize,
1251    pub dead_extras: usize,
1252    pub extra_capacity: usize,
1253}
1254
1255#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1256struct RuleContextFrame {
1257    rule_index: usize,
1258    invoking_state: isize,
1259}
1260
1261#[derive(Clone, Debug, Eq, PartialEq)]
1262struct RecognizeOutcome {
1263    index: usize,
1264    consumed_eof: bool,
1265    alt_number: usize,
1266    member_values: BTreeMap<usize, i64>,
1267    return_values: BTreeMap<String, i64>,
1268    diagnostics: DiagnosticSeqId,
1269    decisions: Vec<usize>,
1270    actions: Vec<ParserAction>,
1271    nodes: NodeSeqId,
1272}
1273
1274#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1275struct FastRecognizeOutcome {
1276    index: usize,
1277    consumed_eof: bool,
1278    diagnostics: DiagnosticSeqId,
1279    deferred_nodes: FastDeferredNodeId,
1280    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1281    /// Copying an outcome copies this compact ID; prepending appends one
1282    /// `SeqLink` without allocating an individual node or list tail.
1283    nodes: NodeSeqId,
1284}
1285
1286#[derive(Debug, Default)]
1287struct FastRecognizeTopScratch {
1288    visiting: FxHashSet<FastRecognizeKey>,
1289    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1290}
1291
1292impl FastRecognizeTopScratch {
1293    fn prepare(&mut self, memo_capacity: usize) {
1294        self.visiting.clear();
1295        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1296        self.memo.clear();
1297        self.memo.reserve(memo_capacity);
1298    }
1299
1300    fn release_oversized_memo(&mut self) {
1301        self.memo.clear();
1302        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1303            self.memo = FxHashMap::default();
1304        }
1305    }
1306}
1307
1308fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1309    buffered_tokens.saturating_mul(8).clamp(
1310        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1311        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1312    )
1313}
1314
1315#[derive(Debug, Default)]
1316struct FastOutcomeDedupScratch {
1317    dense_words: Vec<u64>,
1318    touched_dense_words: Vec<u32>,
1319    sparse_keys: FxHashSet<(usize, bool)>,
1320}
1321
1322/// Handle into the parser-owned deferred tree rope.
1323///
1324/// The sentinel keeps outcomes and repetition paths compact without an
1325/// `Option` discriminant or per-node reference counting.
1326#[repr(transparent)]
1327#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1328struct FastDeferredNodeId(u32);
1329
1330impl FastDeferredNodeId {
1331    const EMPTY: Self = Self(u32::MAX);
1332
1333    const fn is_empty(self) -> bool {
1334        self.0 == Self::EMPTY.0
1335    }
1336}
1337
1338impl Default for FastDeferredNodeId {
1339    fn default() -> Self {
1340        Self::EMPTY
1341    }
1342}
1343
1344#[repr(transparent)]
1345#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1346struct FastDeferredRuleId(u32);
1347
1348/// One immutable deferred-tree rope record in `RecognitionArena`.
1349#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1350enum FastDeferredNode {
1351    Fragment(NodeSeqId),
1352    Rule(FastDeferredRuleId),
1353    Concat {
1354        prefix: FastDeferredNodeId,
1355        suffix: FastDeferredNodeId,
1356    },
1357}
1358
1359#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1360struct FastDeferredRule {
1361    rule_index: u32,
1362    invoking_state: i32,
1363    start_index: u32,
1364    stop_index: Option<u32>,
1365    deferred_children: FastDeferredNodeId,
1366    children: NodeSeqId,
1367}
1368
1369#[repr(transparent)]
1370#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1371struct RecognizedNodeId(u32);
1372
1373#[repr(transparent)]
1374#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1375struct NodeSeqId(u32);
1376
1377impl NodeSeqId {
1378    const EMPTY: Self = Self(u32::MAX);
1379
1380    const fn is_empty(self) -> bool {
1381        self.0 == Self::EMPTY.0
1382    }
1383}
1384
1385impl Default for NodeSeqId {
1386    fn default() -> Self {
1387        Self::EMPTY
1388    }
1389}
1390
1391#[repr(transparent)]
1392#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1393struct DiagnosticSeqId(u32);
1394
1395impl DiagnosticSeqId {
1396    const EMPTY: Self = Self(u32::MAX);
1397
1398    const fn is_empty(self) -> bool {
1399        self.0 == Self::EMPTY.0
1400    }
1401}
1402
1403impl Default for DiagnosticSeqId {
1404    fn default() -> Self {
1405        Self::EMPTY
1406    }
1407}
1408
1409#[repr(transparent)]
1410#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1411struct RecognitionExtraId(u32);
1412
1413#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1414struct SeqLink {
1415    head: RecognizedNodeId,
1416    tail: NodeSeqId,
1417}
1418
1419#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1420struct DiagnosticLink {
1421    head: RecognitionExtraId,
1422    tail: DiagnosticSeqId,
1423}
1424
1425struct ArenaRuleSpec {
1426    rule_index: usize,
1427    invoking_state: isize,
1428    alt_number: usize,
1429    start_index: usize,
1430    stop_index: Option<usize>,
1431    return_values: BTreeMap<String, i64>,
1432    children: NodeSeqId,
1433}
1434
1435/// Compact speculative node record. Common records contain only IDs and
1436/// scalars; missing-token text and generated return values live in `extras`.
1437#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1438enum ArenaRecognizedNode {
1439    Token {
1440        token: TokenId,
1441    },
1442    ErrorToken {
1443        token: TokenId,
1444    },
1445    MissingToken {
1446        extra: RecognitionExtraId,
1447    },
1448    Rule {
1449        rule_index: u32,
1450        invoking_state: i32,
1451        alt_number: u32,
1452        start_index: u32,
1453        stop_index: Option<u32>,
1454        return_values: Option<RecognitionExtraId>,
1455        children: NodeSeqId,
1456    },
1457    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1458    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1459    /// public rule entry hands the tree to the caller.
1460    LeftRecursiveBoundary {
1461        rule_index: u32,
1462    },
1463}
1464
1465#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1466enum RecognitionExtra {
1467    MissingToken {
1468        token_type: i32,
1469        at_index: u32,
1470        text: String,
1471    },
1472    ReturnValues(BTreeMap<String, i64>),
1473    Diagnostic(ParserDiagnostic),
1474}
1475
1476#[derive(Debug, Default)]
1477struct RecognitionArena {
1478    nodes: Vec<ArenaRecognizedNode>,
1479    seq_links: Vec<SeqLink>,
1480    diagnostic_links: Vec<DiagnosticLink>,
1481    extras: Vec<RecognitionExtra>,
1482    deferred_nodes: Vec<FastDeferredNode>,
1483    deferred_rules: Vec<FastDeferredRule>,
1484}
1485
1486// Preserve normal parser reuse while preventing one pathological parse from
1487// pinning an arbitrarily large arena for the parser's remaining lifetime.
1488const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1489const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1490const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1491const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1492const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1493const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1494
1495impl RecognitionArena {
1496    fn reset(&mut self) {
1497        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1498        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1499        reset_arena_vec(
1500            &mut self.diagnostic_links,
1501            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1502        );
1503        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1504        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1505        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1506    }
1507
1508    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1509        let id = RecognizedNodeId(
1510            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1511        );
1512        self.nodes.push(node);
1513        id
1514    }
1515
1516    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1517        let id = RecognitionExtraId(
1518            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1519        );
1520        self.extras.push(extra);
1521        id
1522    }
1523
1524    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1525        let id = NodeSeqId(
1526            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1527        );
1528        self.seq_links.push(SeqLink { head, tail });
1529        id
1530    }
1531
1532    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1533        let id = FastDeferredNodeId(
1534            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1535        );
1536        self.deferred_nodes.push(node);
1537        id
1538    }
1539
1540    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1541        let id = FastDeferredRuleId(
1542            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1543        );
1544        self.deferred_rules.push(rule);
1545        id
1546    }
1547
1548    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1549        if nodes.is_empty() {
1550            FastDeferredNodeId::EMPTY
1551        } else {
1552            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1553        }
1554    }
1555
1556    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1557        let rule = self.push_deferred_rule(rule);
1558        self.push_deferred_node(FastDeferredNode::Rule(rule))
1559    }
1560
1561    fn concat_deferred_nodes(
1562        &mut self,
1563        prefix: FastDeferredNodeId,
1564        suffix: FastDeferredNodeId,
1565    ) -> FastDeferredNodeId {
1566        if prefix.is_empty() {
1567            return suffix;
1568        }
1569        if suffix.is_empty() {
1570            return prefix;
1571        }
1572        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1573    }
1574
1575    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1576        self.deferred_nodes[id.0 as usize]
1577    }
1578
1579    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1580        self.deferred_rules[id.0 as usize]
1581    }
1582
1583    fn prepend_diagnostic(
1584        &mut self,
1585        tail: DiagnosticSeqId,
1586        diagnostic: ParserDiagnostic,
1587    ) -> DiagnosticSeqId {
1588        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1589        self.prepend_diagnostic_id(tail, head)
1590    }
1591
1592    fn prepend_diagnostic_id(
1593        &mut self,
1594        tail: DiagnosticSeqId,
1595        head: RecognitionExtraId,
1596    ) -> DiagnosticSeqId {
1597        let id = DiagnosticSeqId(
1598            u32::try_from(self.diagnostic_links.len())
1599                .expect("diagnostic sequence arena fits in u32"),
1600        );
1601        self.diagnostic_links.push(DiagnosticLink { head, tail });
1602        id
1603    }
1604
1605    fn concat_diagnostics(
1606        &mut self,
1607        prefix: DiagnosticSeqId,
1608        mut suffix: DiagnosticSeqId,
1609    ) -> DiagnosticSeqId {
1610        if prefix.is_empty() {
1611            return suffix;
1612        }
1613        if suffix.is_empty() {
1614            return prefix;
1615        }
1616        let mut reversed = DiagnosticSeqId::EMPTY;
1617        let mut cursor = prefix;
1618        while let Some(link) = self.diagnostic_link(cursor) {
1619            reversed = self.prepend_diagnostic_id(reversed, link.head);
1620            cursor = link.tail;
1621        }
1622        while let Some(link) = self.diagnostic_link(reversed) {
1623            suffix = self.prepend_diagnostic_id(suffix, link.head);
1624            reversed = link.tail;
1625        }
1626        suffix
1627    }
1628
1629    #[cfg(test)]
1630    fn diagnostic_sequence(
1631        &mut self,
1632        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1633    ) -> DiagnosticSeqId {
1634        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1635        let mut sequence = DiagnosticSeqId::EMPTY;
1636        for diagnostic in diagnostics.into_iter().rev() {
1637            sequence = self.prepend_diagnostic(sequence, diagnostic);
1638        }
1639        sequence
1640    }
1641
1642    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1643        self.nodes[id.0 as usize]
1644    }
1645
1646    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1647        &self.extras[id.0 as usize]
1648    }
1649
1650    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1651        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1652    }
1653
1654    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1655        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1656    }
1657
1658    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1659        NodeSeqIter {
1660            arena: self,
1661            cursor: sequence,
1662        }
1663    }
1664
1665    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1666        DiagnosticSeqIter {
1667            arena: self,
1668            cursor: sequence,
1669        }
1670    }
1671
1672    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1673        self.diagnostics(sequence).count()
1674    }
1675
1676    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1677        self.diagnostics(sequence)
1678            .filter(|diagnostic| {
1679                diagnostic.message.starts_with("mismatched input ")
1680                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1681            })
1682            .count()
1683    }
1684
1685    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1686        self.diagnostics(left).cmp(self.diagnostics(right))
1687    }
1688
1689    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1690        self.iter(sequence).count()
1691    }
1692
1693    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1694        self.iter(sequence).any(|node| match self.node(node) {
1695            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1696            ArenaRecognizedNode::Rule { children, .. } => {
1697                self.sequence_has_left_recursive_boundary(children)
1698            }
1699            ArenaRecognizedNode::Token { .. }
1700            | ArenaRecognizedNode::ErrorToken { .. }
1701            | ArenaRecognizedNode::MissingToken { .. } => false,
1702        })
1703    }
1704
1705    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1706        self.iter(sequence).any(|node| {
1707            matches!(
1708                self.node(node),
1709                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1710            )
1711        })
1712    }
1713
1714    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1715        self.iter(sequence).any(|node| {
1716            matches!(
1717                self.node(node),
1718                ArenaRecognizedNode::Token { .. }
1719                    | ArenaRecognizedNode::ErrorToken { .. }
1720                    | ArenaRecognizedNode::MissingToken { .. }
1721            )
1722        })
1723    }
1724
1725    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1726        match self.node(node) {
1727            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1728                Some(token.index())
1729            }
1730            ArenaRecognizedNode::MissingToken { extra } => {
1731                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1732                    unreachable!("missing-token node must reference missing-token extra");
1733                };
1734                Some(*at_index as usize)
1735            }
1736            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1737            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1738        }
1739    }
1740
1741    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
1742        match self.node(node) {
1743            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1744                Some(token.index())
1745            }
1746            ArenaRecognizedNode::MissingToken { extra } => {
1747                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1748                    unreachable!("missing-token node must reference missing-token extra");
1749                };
1750                (*at_index as usize).checked_sub(1)
1751            }
1752            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
1753            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1754        }
1755    }
1756
1757    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
1758        let start = self.node_start_index(node)?;
1759        let stop = self.node_stop_index(node);
1760        Some((start, stop))
1761    }
1762
1763    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
1764        self.iter(sequence)
1765            .find_map(|node| self.node_start_index(node))
1766    }
1767
1768    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
1769        let mut stop = None;
1770        for node in self.iter(sequence) {
1771            if let Some(index) = self.node_stop_index(node) {
1772                stop = Some(index);
1773            }
1774        }
1775        stop
1776    }
1777
1778    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
1779        self.iter(sequence)
1780            .any(|node| self.node_needs_stable_tie(node))
1781    }
1782
1783    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
1784        match self.node(node) {
1785            ArenaRecognizedNode::Token { .. }
1786            | ArenaRecognizedNode::ErrorToken { .. }
1787            | ArenaRecognizedNode::MissingToken { .. } => false,
1788            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1789            ArenaRecognizedNode::Rule {
1790                rule_index,
1791                children,
1792                ..
1793            } => self.iter(children).any(|child| {
1794                matches!(
1795                    self.node(child),
1796                    ArenaRecognizedNode::Rule {
1797                        rule_index: child_rule,
1798                        ..
1799                    } if child_rule == rule_index
1800                ) || self.node_needs_stable_tie(child)
1801            }),
1802        }
1803    }
1804
1805    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
1806        loop {
1807            match (self.link(left), self.link(right)) {
1808                (Some(left_link), Some(right_link)) => {
1809                    let order = self.compare_nodes(left_link.head, right_link.head);
1810                    if order != Ordering::Equal {
1811                        return order;
1812                    }
1813                    left = left_link.tail;
1814                    right = right_link.tail;
1815                }
1816                (None, None) => return Ordering::Equal,
1817                (None, Some(_)) => return Ordering::Less,
1818                (Some(_), None) => return Ordering::Greater,
1819            }
1820        }
1821    }
1822
1823    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
1824        let left = self.node(left);
1825        let right = self.node(right);
1826        match (left, right) {
1827            (
1828                ArenaRecognizedNode::Token { token: left },
1829                ArenaRecognizedNode::Token { token: right },
1830            )
1831            | (
1832                ArenaRecognizedNode::ErrorToken { token: left },
1833                ArenaRecognizedNode::ErrorToken { token: right },
1834            ) => left.cmp(&right),
1835            (
1836                ArenaRecognizedNode::MissingToken { extra: left },
1837                ArenaRecognizedNode::MissingToken { extra: right },
1838            ) => self.extra(left).cmp(self.extra(right)),
1839            (
1840                ArenaRecognizedNode::Rule {
1841                    rule_index: left_rule,
1842                    invoking_state: left_invoking,
1843                    alt_number: left_alt,
1844                    start_index: left_start,
1845                    stop_index: left_stop,
1846                    return_values: left_returns,
1847                    children: left_children,
1848                },
1849                ArenaRecognizedNode::Rule {
1850                    rule_index: right_rule,
1851                    invoking_state: right_invoking,
1852                    alt_number: right_alt,
1853                    start_index: right_start,
1854                    stop_index: right_stop,
1855                    return_values: right_returns,
1856                    children: right_children,
1857                },
1858            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
1859                .cmp(&(
1860                    right_rule,
1861                    right_invoking,
1862                    right_alt,
1863                    right_start,
1864                    right_stop,
1865                ))
1866                .then_with(|| {
1867                    left_returns
1868                        .map(|id| self.extra(id))
1869                        .cmp(&right_returns.map(|id| self.extra(id)))
1870                })
1871                .then_with(|| self.compare_sequences(left_children, right_children)),
1872            (
1873                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: left },
1874                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: right },
1875            ) => left.cmp(&right),
1876            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
1877        }
1878    }
1879
1880    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1881        let mut reversed = NodeSeqId::EMPTY;
1882        while let Some(link) = self.link(sequence) {
1883            reversed = self.prepend(reversed, link.head);
1884            sequence = link.tail;
1885        }
1886        reversed
1887    }
1888
1889    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
1890        if !self.sequence_has_direct_boundary(sequence) {
1891            return sequence;
1892        }
1893        let mut reversed = NodeSeqId::EMPTY;
1894        while let Some(link) = self.link(sequence) {
1895            match self.node(link.head) {
1896                ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
1897                    if !reversed.is_empty() {
1898                        let children = self.reverse_sequence(reversed);
1899                        let start_index = self.sequence_start_index(children).unwrap_or_default();
1900                        let stop_index = self.sequence_stop_index(children);
1901                        let rule = self.push_node(ArenaRecognizedNode::Rule {
1902                            rule_index,
1903                            invoking_state: -1,
1904                            alt_number: 0,
1905                            start_index: u32::try_from(start_index)
1906                                .expect("left-recursive start index fits in u32"),
1907                            stop_index: stop_index.map(|index| {
1908                                u32::try_from(index).expect("left-recursive stop index fits in u32")
1909                            }),
1910                            return_values: None,
1911                            children,
1912                        });
1913                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
1914                    }
1915                }
1916                _ => {
1917                    reversed = self.prepend(reversed, link.head);
1918                }
1919            }
1920            sequence = link.tail;
1921        }
1922        self.reverse_sequence(reversed)
1923    }
1924
1925    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
1926        let mut live_nodes = vec![false; self.nodes.len()];
1927        let mut live_links = vec![false; self.seq_links.len()];
1928        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
1929        let mut live_extras = vec![false; self.extras.len()];
1930        let mut pending = vec![root];
1931        while let Some(mut sequence) = pending.pop() {
1932            while let Some(link) = self.link(sequence) {
1933                let link_index = sequence.0 as usize;
1934                if live_links[link_index] {
1935                    break;
1936                }
1937                live_links[link_index] = true;
1938                let node_index = link.head.0 as usize;
1939                if !live_nodes[node_index] {
1940                    live_nodes[node_index] = true;
1941                    match self.node(link.head) {
1942                        ArenaRecognizedNode::MissingToken { extra } => {
1943                            live_extras[extra.0 as usize] = true;
1944                        }
1945                        ArenaRecognizedNode::Rule {
1946                            return_values,
1947                            children,
1948                            ..
1949                        } => {
1950                            if let Some(extra) = return_values {
1951                                live_extras[extra.0 as usize] = true;
1952                            }
1953                            pending.push(children);
1954                        }
1955                        ArenaRecognizedNode::Token { .. }
1956                        | ArenaRecognizedNode::ErrorToken { .. }
1957                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
1958                    }
1959                }
1960                sequence = link.tail;
1961            }
1962        }
1963        let mut diagnostics = diagnostics;
1964        while let Some(link) = self.diagnostic_link(diagnostics) {
1965            let link_index = diagnostics.0 as usize;
1966            if live_diagnostic_links[link_index] {
1967                break;
1968            }
1969            live_diagnostic_links[link_index] = true;
1970            live_extras[link.head.0 as usize] = true;
1971            diagnostics = link.tail;
1972        }
1973        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
1974        let live_link_count = live_links.into_iter().filter(|live| *live).count()
1975            + live_diagnostic_links
1976                .into_iter()
1977                .filter(|live| *live)
1978                .count();
1979        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
1980        let total_links = self.seq_links.len() + self.diagnostic_links.len();
1981        RecognitionArenaStats {
1982            total_nodes: self.nodes.len(),
1983            live_nodes: live_node_count,
1984            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
1985            node_capacity: self.nodes.capacity(),
1986            total_links,
1987            live_links: live_link_count,
1988            dead_links: total_links.saturating_sub(live_link_count),
1989            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
1990            total_extras: self.extras.len(),
1991            live_extras: live_extra_count,
1992            dead_extras: self.extras.len().saturating_sub(live_extra_count),
1993            extra_capacity: self.extras.capacity(),
1994        }
1995    }
1996}
1997
1998fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
1999    if storage.capacity() > max_retained_capacity {
2000        *storage = Vec::new();
2001    } else {
2002        storage.clear();
2003    }
2004}
2005
2006const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2007    match node {
2008        ArenaRecognizedNode::Token { .. } => 0,
2009        ArenaRecognizedNode::ErrorToken { .. } => 1,
2010        ArenaRecognizedNode::MissingToken { .. } => 2,
2011        ArenaRecognizedNode::Rule { .. } => 3,
2012        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2013    }
2014}
2015
2016struct NodeSeqIter<'a> {
2017    arena: &'a RecognitionArena,
2018    cursor: NodeSeqId,
2019}
2020
2021impl Iterator for NodeSeqIter<'_> {
2022    type Item = RecognizedNodeId;
2023
2024    fn next(&mut self) -> Option<Self::Item> {
2025        let link = self.arena.link(self.cursor)?;
2026        self.cursor = link.tail;
2027        Some(link.head)
2028    }
2029}
2030
2031struct DiagnosticSeqIter<'a> {
2032    arena: &'a RecognitionArena,
2033    cursor: DiagnosticSeqId,
2034}
2035
2036impl<'a> Iterator for DiagnosticSeqIter<'a> {
2037    type Item = &'a ParserDiagnostic;
2038
2039    fn next(&mut self) -> Option<Self::Item> {
2040        let link = self.arena.diagnostic_link(self.cursor)?;
2041        self.cursor = link.tail;
2042        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2043            unreachable!("diagnostic link must reference diagnostic extra");
2044        };
2045        Some(diagnostic)
2046    }
2047}
2048
2049#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2050struct ParserDiagnostic {
2051    line: usize,
2052    column: usize,
2053    message: String,
2054}
2055
2056#[derive(Clone, Debug, Default, Eq, PartialEq)]
2057struct ExpectedTokens {
2058    index: Option<usize>,
2059    symbols: BTreeSet<i32>,
2060    no_viable: Option<NoViableAlternative>,
2061}
2062
2063#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2064struct NoViableAlternative {
2065    start_index: usize,
2066    error_index: usize,
2067}
2068
2069impl ExpectedTokens {
2070    /// Records the expected symbols for the farthest token index reached by any
2071    /// failed ATN path.
2072    fn record_transition(
2073        &mut self,
2074        index: usize,
2075        transition: ParserTransition<'_>,
2076        max_token_type: i32,
2077    ) {
2078        let symbols = transition_expected_symbols(transition, max_token_type);
2079        match self.index {
2080            Some(current) if index < current => {}
2081            Some(current) if index == current => self.symbols.extend(symbols),
2082            _ => {
2083                self.index = Some(index);
2084                self.symbols = symbols;
2085            }
2086        }
2087    }
2088
2089    /// Records an ambiguous decision that failed after consuming a shared
2090    /// prefix, which ANTLR reports as `no viable alternative`.
2091    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2092        match self.no_viable {
2093            Some(current) if error_index < current.error_index => {}
2094            _ => {
2095                self.no_viable = Some(NoViableAlternative {
2096                    start_index,
2097                    error_index,
2098                });
2099            }
2100        }
2101    }
2102}
2103
2104/// Compact token-type set for parser-internal FIRST/lookahead caches.
2105///
2106/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2107/// but the hot recognizer path mostly needs `contains` and set union over
2108/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2109/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2110#[derive(Clone, Debug, Default, Eq, PartialEq)]
2111struct TokenBitSet {
2112    words: Vec<u64>,
2113}
2114
2115impl TokenBitSet {
2116    fn insert(&mut self, symbol: i32) {
2117        let Some(slot) = token_bit_slot(symbol) else {
2118            return;
2119        };
2120        let word = slot / u64::BITS as usize;
2121        if word >= self.words.len() {
2122            self.words.resize(word + 1, 0);
2123        }
2124        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2125    }
2126
2127    fn extend_range(&mut self, start: i32, stop: i32) {
2128        let (start, stop) = if start <= stop {
2129            (start, stop)
2130        } else {
2131            (stop, start)
2132        };
2133        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2134            self.insert(TOKEN_EOF);
2135        }
2136        let positive_start = start.max(1);
2137        if positive_start > stop {
2138            return;
2139        }
2140        let Some(start_slot) = token_bit_slot(positive_start) else {
2141            return;
2142        };
2143        let Some(stop_slot) = token_bit_slot(stop) else {
2144            return;
2145        };
2146        self.extend_slot_range(start_slot, stop_slot);
2147    }
2148
2149    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2150        if start_slot > stop_slot {
2151            return;
2152        }
2153        let start_word = start_slot / u64::BITS as usize;
2154        let stop_word = stop_slot / u64::BITS as usize;
2155        if stop_word >= self.words.len() {
2156            self.words.resize(stop_word + 1, 0);
2157        }
2158        let start_offset = start_slot % u64::BITS as usize;
2159        let stop_offset = stop_slot % u64::BITS as usize;
2160        if start_word == stop_word {
2161            self.words[start_word] |=
2162                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2163            return;
2164        }
2165        self.words[start_word] |= !0_u64 << start_offset;
2166        for word in &mut self.words[(start_word + 1)..stop_word] {
2167            *word = !0_u64;
2168        }
2169        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2170    }
2171
2172    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2173        for symbol in symbols {
2174            self.insert(symbol);
2175        }
2176    }
2177
2178    fn extend_from(&mut self, other: &Self) {
2179        if other.words.len() > self.words.len() {
2180            self.words.resize(other.words.len(), 0);
2181        }
2182        for (left, right) in self.words.iter_mut().zip(&other.words) {
2183            *left |= *right;
2184        }
2185    }
2186
2187    fn contains(&self, symbol: i32) -> bool {
2188        let Some(slot) = token_bit_slot(symbol) else {
2189            return false;
2190        };
2191        let word = slot / u64::BITS as usize;
2192        self.words
2193            .get(word)
2194            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2195    }
2196
2197    fn is_empty(&self) -> bool {
2198        self.words.iter().all(|word| *word == 0)
2199    }
2200
2201    fn symbols(&self) -> impl Iterator<Item = i32> + '_ {
2202        self.words
2203            .iter()
2204            .copied()
2205            .enumerate()
2206            .flat_map(|(word_index, mut bits)| {
2207                std::iter::from_fn(move || {
2208                    while bits != 0 {
2209                        let bit = bits.trailing_zeros() as usize;
2210                        bits &= bits - 1;
2211                        if let Some(symbol) =
2212                            token_bit_symbol(word_index * u64::BITS as usize + bit)
2213                        {
2214                            return Some(symbol);
2215                        }
2216                    }
2217                    None
2218                })
2219            })
2220    }
2221
2222    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2223        target.extend(self.symbols());
2224    }
2225
2226    fn to_btree_set(&self) -> BTreeSet<i32> {
2227        let mut out = BTreeSet::new();
2228        self.extend_btree_set(&mut out);
2229        out
2230    }
2231}
2232
2233fn token_bit_slot(symbol: i32) -> Option<usize> {
2234    if symbol == TOKEN_EOF {
2235        Some(0)
2236    } else if symbol > 0 {
2237        usize::try_from(symbol).ok()
2238    } else {
2239        None
2240    }
2241}
2242
2243fn token_bit_symbol(slot: usize) -> Option<i32> {
2244    if slot == 0 {
2245        Some(TOKEN_EOF)
2246    } else {
2247        i32::try_from(slot).ok()
2248    }
2249}
2250
2251/// Converts one consuming transition into the token types that would satisfy it
2252/// for diagnostic reporting.
2253fn transition_expected_symbols(
2254    transition: ParserTransition<'_>,
2255    max_token_type: i32,
2256) -> BTreeSet<i32> {
2257    let mut symbols = BTreeSet::new();
2258    match &transition.data() {
2259        Transition::Atom { label, .. } => {
2260            symbols.insert(*label);
2261        }
2262        Transition::Range { start, stop, .. } => {
2263            symbols.extend(*start..=*stop);
2264        }
2265        Transition::Set { set, .. } => {
2266            for (start, stop) in set.ranges() {
2267                symbols.extend(start..=stop);
2268            }
2269        }
2270        Transition::NotSet { set, .. } => {
2271            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2272        }
2273        Transition::Wildcard { .. } => {
2274            symbols.extend(1..=max_token_type);
2275        }
2276        Transition::Epsilon { .. }
2277        | Transition::Rule { .. }
2278        | Transition::Predicate { .. }
2279        | Transition::Action { .. }
2280        | Transition::Precedence { .. } => {}
2281    }
2282    symbols
2283}
2284
2285fn transition_expected_token_set(
2286    transition: ParserTransition<'_>,
2287    max_token_type: i32,
2288) -> TokenBitSet {
2289    let mut symbols = TokenBitSet::default();
2290    match &transition.data() {
2291        Transition::Atom { label, .. } => {
2292            symbols.insert(*label);
2293        }
2294        Transition::Range { start, stop, .. } => {
2295            symbols.extend_range(*start, *stop);
2296        }
2297        Transition::Set { set, .. } => {
2298            for (start, stop) in set.ranges() {
2299                symbols.extend_range(start, stop);
2300            }
2301        }
2302        Transition::NotSet { set, .. } => {
2303            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2304        }
2305        Transition::Wildcard { .. } => {
2306            symbols.extend_range(1, max_token_type);
2307        }
2308        Transition::Epsilon { .. }
2309        | Transition::Rule { .. }
2310        | Transition::Predicate { .. }
2311        | Transition::Action { .. }
2312        | Transition::Precedence { .. } => {}
2313    }
2314    symbols
2315}
2316
2317/// Returns the consuming-token expectations reachable from an ATN state through
2318/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2319/// and loop exits report the same expectation set ANTLR users see.
2320fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2321    let mut symbols = BTreeSet::new();
2322    let mut stack = vec![state_number];
2323    let mut visited = BTreeSet::new();
2324    while let Some(current) = stack.pop() {
2325        if !visited.insert(current) {
2326            continue;
2327        }
2328        let Some(state) = atn.state(current) else {
2329            continue;
2330        };
2331        for transition in &state.transitions() {
2332            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2333            if transition_symbols.is_empty() {
2334                if transition.is_epsilon() {
2335                    stack.push(transition.target());
2336                }
2337            } else {
2338                symbols.extend(transition_symbols);
2339            }
2340        }
2341    }
2342    symbols
2343}
2344
2345fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2346    let mut symbols = TokenBitSet::default();
2347    let mut stack = vec![state_number];
2348    let mut visited = BTreeSet::new();
2349    while let Some(current) = stack.pop() {
2350        if !visited.insert(current) {
2351            continue;
2352        }
2353        let Some(state) = atn.state(current) else {
2354            continue;
2355        };
2356        for transition in &state.transitions() {
2357            let transition_symbols =
2358                transition_expected_token_set(transition, atn.max_token_type());
2359            if transition_symbols.is_empty() {
2360                if transition.is_epsilon() {
2361                    stack.push(transition.target());
2362                }
2363            } else {
2364                symbols.extend_from(&transition_symbols);
2365            }
2366        }
2367    }
2368    symbols
2369}
2370
2371fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2372    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2373        return false;
2374    };
2375    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2376        return false;
2377    };
2378    epsilon_reaches_state(atn, state_number, stop_state)
2379}
2380
2381fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2382    let mut stack = vec![start];
2383    let mut visited = BTreeSet::new();
2384    while let Some(current) = stack.pop() {
2385        if current == target {
2386            return true;
2387        }
2388        if !visited.insert(current) {
2389            continue;
2390        }
2391        let Some(state) = atn.state(current) else {
2392            continue;
2393        };
2394        stack.extend(
2395            state
2396                .transitions()
2397                .iter()
2398                .filter(|transition| transition.is_epsilon())
2399                .map(ParserTransition::target),
2400        );
2401    }
2402    false
2403}
2404
2405/// FIRST set for a rule entry plus whether the rule is nullable.
2406///
2407/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2408/// a consuming transition or reaches the rule's stop state. Used by the fast
2409/// recognizer to skip rule alternatives whose first-consumed token cannot
2410/// possibly match the current lookahead.
2411#[derive(Clone, Debug, Default, Eq, PartialEq)]
2412struct FirstSet {
2413    symbols: TokenBitSet,
2414    nullable: bool,
2415}
2416
2417/// Per-parser cache of FIRST sets computed during recognition. The fast path
2418/// consults this on every speculative `Transition::Rule` encounter, so the
2419/// computation must amortize across all of those calls — the FIRST set is a
2420/// pure function of the ATN, not of the input position. Cached entries are
2421/// shared via `Rc` so the recognizer never deep-copies the underlying
2422/// `BTreeSet<i32>`.
2423type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2424
2425// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2426// and decision-lookahead entries are purely functions of the grammar's
2427// ATN, so caching across parses lets repeated parsing of the same grammar
2428// (the common case for a CLI tool or language server) avoid redoing the
2429// closure work. Generated parsers hand us a `&'static Atn` whose address
2430// is stable, which is what we hash on.
2431type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2432
2433#[derive(Debug, Default)]
2434struct LeftRecursiveOperatorLookahead {
2435    /// Operator alts whose token-prefix is fully matched by this one symbol
2436    /// (then only epsilons/actions remain before the recursive RHS call).
2437    /// Safe for one-token loop-enter fast path.
2438    single_token: TokenBitSet,
2439    /// Operator alts that start with this symbol but still require more tokens
2440    /// before the operand. Must not force enter from one-token lookahead when a
2441    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2442    /// has to weigh the exit alt as well.
2443    multi_token_prefix: TokenBitSet,
2444    predicate_dependent: TokenBitSet,
2445}
2446
2447#[derive(Default)]
2448struct SharedAtnCache {
2449    first_set: FirstSetCache,
2450    decision_lookahead: DecisionLookaheadCache,
2451    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2452    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2453    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2454    rule_stop_reach: FxHashMap<usize, bool>,
2455    observable_action_transitions: Option<bool>,
2456    predicate_transitions: Option<bool>,
2457}
2458
2459thread_local! {
2460    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2461        RefCell::new(FxHashMap::default());
2462}
2463
2464/// Compound key for `SHARED_ATN_CACHES`.
2465///
2466/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2467/// pointer identifies one grammar for the program's lifetime. For the
2468/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2469/// the secondary fields below catch the pointer collision: a new grammar
2470/// would need to match all of `(states ptr, states len, max_token_type)` to
2471/// be mistaken for the dropped one. That combination changing under us
2472/// without a rebuild is implausible enough to treat as a bug; bundling them
2473/// into the key is otherwise a few extra bytes per lookup.
2474#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2475struct SharedAtnCacheKey {
2476    atn: usize,
2477    states: usize,
2478    state_count: usize,
2479    max_token_type: i32,
2480}
2481
2482impl SharedAtnCacheKey {
2483    fn for_atn(atn: &Atn) -> Self {
2484        let (states, state_count) = atn.storage_identity();
2485        Self {
2486            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2487            states,
2488            state_count,
2489            max_token_type: atn.max_token_type(),
2490        }
2491    }
2492}
2493
2494fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2495    SHARED_ATN_CACHES.with(|cell| {
2496        let key = SharedAtnCacheKey::for_atn(atn);
2497        let mut map = cell.borrow_mut();
2498        let cache = map.entry(key).or_default();
2499        f(&mut cache.first_set)
2500    })
2501}
2502
2503fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2504    SHARED_ATN_CACHES.with(|cell| {
2505        let key = SharedAtnCacheKey::for_atn(atn);
2506        let mut map = cell.borrow_mut();
2507        let cache = map.entry(key).or_default();
2508        f(cache)
2509    })
2510}
2511
2512/// Per-decision-state cached look-1 sets for each outgoing transition.
2513///
2514/// At a multi-alternative state, the recognizer would otherwise speculatively
2515/// walk every alternative even when only one can possibly accept the current
2516/// lookahead. Caching the look-1 set per transition lets us prune the
2517/// non-viable transitions before recursing — the same SLL prediction trick
2518/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2519/// filter rather than a full DFA.
2520#[derive(Debug, Default)]
2521struct DecisionLookahead {
2522    transitions: Vec<TransitionLookSet>,
2523}
2524
2525/// Look-1 information for one outgoing transition.
2526///
2527/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2528/// can reach the rule stop without consuming a token (e.g. an empty alt).
2529/// Nullable transitions cannot be pruned: they may still be the right path
2530/// when the lookahead consumes nothing further inside the current rule.
2531#[derive(Clone, Debug, Default)]
2532struct TransitionLookSet {
2533    symbols: TokenBitSet,
2534    nullable: bool,
2535}
2536
2537/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2538/// rarely-touched fields keeps the recursive helpers below the function-arity
2539/// lint and lets every nested call thread the same cache and cycle guards.
2540struct FirstSetCtx<'a> {
2541    cache: &'a mut FirstSetCache,
2542    in_progress: BTreeSet<(usize, usize)>,
2543    hit_cycle: bool,
2544}
2545
2546/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2547/// shared cache and tolerating recursive nullable rule chains. Mutually
2548/// recursive rules cannot stack-overflow because callers in flight are tracked
2549/// in `ctx.in_progress`; revisits return without recursing, and the partial
2550/// result is cached only when no cycle was detected during its computation.
2551///
2552/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2553/// rule-call probes only pay a reference bump.
2554fn rule_first_set(
2555    atn: &Atn,
2556    target: usize,
2557    rule_stop_state: usize,
2558    cache: &mut FirstSetCache,
2559) -> Rc<FirstSet> {
2560    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2561        return Rc::clone(cached);
2562    }
2563    let mut ctx = FirstSetCtx {
2564        cache,
2565        in_progress: BTreeSet::new(),
2566        hit_cycle: false,
2567    };
2568    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2569}
2570
2571fn rule_first_set_cached(
2572    atn: &Atn,
2573    target: usize,
2574    rule_stop_state: usize,
2575    ctx: &mut FirstSetCtx<'_>,
2576) -> Rc<FirstSet> {
2577    let key = (target, rule_stop_state);
2578    if let Some(cached) = ctx.cache.get(&key) {
2579        return Rc::clone(cached);
2580    }
2581    if !ctx.in_progress.insert(key) {
2582        // Cycle: a caller above is already computing this entry. Return an
2583        // empty FIRST set; that caller's traversal supplies the contributions
2584        // from the rule's other alternatives.
2585        return Rc::new(FirstSet::default());
2586    }
2587    let saved_hit_cycle = ctx.hit_cycle;
2588    ctx.hit_cycle = false;
2589    let mut first = FirstSet::default();
2590    let mut visited = BTreeSet::new();
2591    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2592    ctx.in_progress.remove(&key);
2593    let entry = Rc::new(first);
2594    if !ctx.hit_cycle {
2595        ctx.cache.insert(key, Rc::clone(&entry));
2596    }
2597    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2598    entry
2599}
2600
2601/// Returns the look-1 set for traversing `transition` while still inside the
2602/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2603/// prunes transitions whose look-1 cannot accept the current lookahead.
2604fn transition_first_set(
2605    atn: &Atn,
2606    transition: ParserTransition<'_>,
2607    rule_stop_state: usize,
2608    cache: &mut FirstSetCache,
2609) -> TransitionLookSet {
2610    match &transition.data() {
2611        Transition::Atom { label, .. } => {
2612            let mut symbols = TokenBitSet::default();
2613            symbols.insert(*label);
2614            TransitionLookSet {
2615                symbols,
2616                nullable: false,
2617            }
2618        }
2619        Transition::Range { start, stop, .. } => {
2620            let mut symbols = TokenBitSet::default();
2621            symbols.extend_range(*start, *stop);
2622            TransitionLookSet {
2623                symbols,
2624                nullable: false,
2625            }
2626        }
2627        Transition::Set { set, .. } => {
2628            let mut symbols = TokenBitSet::default();
2629            for (start, stop) in set.ranges() {
2630                symbols.extend_range(start, stop);
2631            }
2632            TransitionLookSet {
2633                symbols,
2634                nullable: false,
2635            }
2636        }
2637        Transition::NotSet { set, .. } => {
2638            let max = atn.max_token_type();
2639            let mut symbols = TokenBitSet::default();
2640            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2641            TransitionLookSet {
2642                symbols,
2643                nullable: false,
2644            }
2645        }
2646        Transition::Wildcard { .. } => {
2647            let mut symbols = TokenBitSet::default();
2648            symbols.extend_range(1, atn.max_token_type());
2649            TransitionLookSet {
2650                symbols,
2651                nullable: false,
2652            }
2653        }
2654        Transition::Epsilon { target }
2655        | Transition::Action { target, .. }
2656        | Transition::Predicate { target, .. }
2657        | Transition::Precedence { target, .. } => {
2658            // Walk the closure starting at `target` until a consuming transition
2659            // is reached or the rule stop state is hit.
2660            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2661            TransitionLookSet {
2662                symbols: first.symbols.clone(),
2663                nullable: first.nullable,
2664            }
2665        }
2666        Transition::Rule {
2667            target,
2668            rule_index,
2669            follow_state,
2670            ..
2671        } => {
2672            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2673                return TransitionLookSet::default();
2674            };
2675            let child = rule_first_set(atn, *target, child_stop, cache);
2676            let mut symbols = child.symbols.clone();
2677            let nullable = if child.nullable {
2678                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2679                symbols.extend_from(&follow.symbols);
2680                follow.nullable
2681            } else {
2682                false
2683            };
2684            TransitionLookSet { symbols, nullable }
2685        }
2686    }
2687}
2688
2689/// Reports whether `transition` can be pruned at a multi-alt state because
2690/// its cached look-1 cannot accept the current lookahead.
2691///
2692/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2693/// Rule/Precedence) so consuming transitions still reach the
2694/// `matches`+recovery path that surfaces single-token deletion / insertion
2695/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2696/// transition is pruned, its FIRST set is folded into `expected` so failed
2697/// parses produce the same `mismatched input ... expecting ...` diagnostic
2698/// the no-prefilter baseline would emit.
2699/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2700/// one transition's FIRST set and no transition is nullable. Used as an
2701/// LL(1) commit point: when prediction is unambiguous from the lookahead
2702/// alone, the recursive recognizer can skip every other alt without paying
2703/// for the per-transition filter probe.
2704///
2705/// `None` signals the caller to fall back to per-transition lookahead
2706/// filtering. Returning `Some` for an alt whose transition cannot actually
2707/// match would prune the only viable parse path; this is why we require
2708/// strict disjointness *and* no nullable transitions in the decision.
2709fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2710    let mut chosen: Option<usize> = None;
2711    for (index, transition) in entry.transitions.iter().enumerate() {
2712        if transition.nullable {
2713            return None;
2714        }
2715        if transition.symbols.contains(symbol) {
2716            if chosen.is_some() {
2717                return None;
2718            }
2719            chosen = Some(index);
2720        }
2721    }
2722    chosen
2723}
2724
2725/// Returns the unique greedy alt index (0-based) selected by the current
2726/// lookahead.
2727///
2728/// The shortcut is intentionally conservative around nullable exits. If the
2729/// current symbol can start a consuming alternative and an empty alternative is
2730/// also present, one-token lookahead is not enough to know whether the symbol
2731/// belongs to the current construct or to its caller's follow set. `None`
2732/// signals the caller to fall back to adaptive prediction.
2733fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2734    let mut matching_non_nullable_alt = None;
2735    let mut nullable_alt = None;
2736    for (index, transition) in entry.transitions.iter().enumerate() {
2737        if transition.nullable {
2738            if nullable_alt.is_some() {
2739                return None;
2740            }
2741            nullable_alt = Some(index);
2742        }
2743        if transition.symbols.contains(symbol) {
2744            if transition.nullable {
2745                continue;
2746            }
2747            if matching_non_nullable_alt.is_some() {
2748                return None;
2749            }
2750            matching_non_nullable_alt = Some(index);
2751        }
2752    }
2753    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
2754        return None;
2755    }
2756    if non_greedy {
2757        nullable_alt.or(matching_non_nullable_alt)
2758    } else {
2759        matching_non_nullable_alt.or(nullable_alt)
2760    }
2761}
2762
2763fn should_skip_via_lookahead(
2764    transition_kind: ParserTransitionKind,
2765    transition_index: usize,
2766    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
2767    index: usize,
2768    record_expected: bool,
2769    expected: &mut ExpectedTokens,
2770) -> bool {
2771    let prune_non_consuming = matches!(
2772        transition_kind,
2773        ParserTransitionKind::Epsilon
2774            | ParserTransitionKind::Action
2775            | ParserTransitionKind::Predicate
2776            | ParserTransitionKind::Rule
2777            | ParserTransitionKind::Precedence
2778    );
2779    if !prune_non_consuming {
2780        return false;
2781    }
2782    let Some((symbol, entry)) = lookahead_filter else {
2783        return false;
2784    };
2785    let Some(set) = entry.transitions.get(transition_index) else {
2786        return false;
2787    };
2788    if set.symbols.contains(*symbol) || set.nullable {
2789        return false;
2790    }
2791    if record_expected && !set.symbols.is_empty() {
2792        record_pruned_transition_expected(set, index, expected);
2793    }
2794    true
2795}
2796
2797fn should_skip_rule_via_first_set(
2798    first: &FirstSet,
2799    symbol: i32,
2800    record_expected: bool,
2801    index: usize,
2802    expected: &mut ExpectedTokens,
2803) -> bool {
2804    if first.nullable || first.symbols.contains(symbol) {
2805        return false;
2806    }
2807    if record_expected && !first.symbols.is_empty() {
2808        record_token_bit_expected(&first.symbols, index, expected);
2809    }
2810    true
2811}
2812
2813fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
2814    match expected.index {
2815        Some(current) if index < current => {}
2816        Some(current) if index == current => {
2817            symbols.extend_btree_set(&mut expected.symbols);
2818        }
2819        _ => {
2820            expected.index = Some(index);
2821            expected.symbols = symbols.to_btree_set();
2822        }
2823    }
2824}
2825
2826/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
2827fn record_pruned_transition_expected(
2828    set: &TransitionLookSet,
2829    index: usize,
2830    expected: &mut ExpectedTokens,
2831) {
2832    match expected.index {
2833        Some(current) if index < current => {}
2834        Some(current) if index == current => {
2835            set.symbols.extend_btree_set(&mut expected.symbols);
2836        }
2837        _ => {
2838            expected.index = Some(index);
2839            expected.symbols = set.symbols.to_btree_set();
2840        }
2841    }
2842}
2843
2844fn rule_first_set_inner(
2845    atn: &Atn,
2846    state_number: usize,
2847    rule_stop_state: usize,
2848    ctx: &mut FirstSetCtx<'_>,
2849    visited: &mut BTreeSet<usize>,
2850    first: &mut FirstSet,
2851) {
2852    if !visited.insert(state_number) {
2853        return;
2854    }
2855    if state_number == rule_stop_state {
2856        first.nullable = true;
2857        return;
2858    }
2859    let Some(state) = atn.state(state_number) else {
2860        return;
2861    };
2862    for transition in &state.transitions() {
2863        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2864        if !transition_symbols.is_empty() {
2865            first.symbols.extend_iter(transition_symbols);
2866            continue;
2867        }
2868        match &transition.data() {
2869            Transition::Epsilon { target }
2870            | Transition::Action { target, .. }
2871            | Transition::Predicate { target, .. }
2872            | Transition::Precedence { target, .. } => {
2873                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
2874            }
2875            Transition::Rule {
2876                target,
2877                rule_index,
2878                follow_state,
2879                ..
2880            } => {
2881                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2882                    continue;
2883                };
2884                let child_key = (*target, child_stop);
2885                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
2886                    ctx.hit_cycle = true;
2887                }
2888                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
2889                first.symbols.extend_from(&child.symbols);
2890                if child.nullable {
2891                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
2892                }
2893            }
2894            Transition::Atom { .. }
2895            | Transition::Range { .. }
2896            | Transition::Set { .. }
2897            | Transition::NotSet { .. }
2898            | Transition::Wildcard { .. } => {}
2899        }
2900    }
2901}
2902
2903/// Returns token types that can resume parsing from `state_number` after a
2904/// failed child rule, following rule calls as well as epsilon transitions.
2905fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
2906    let mut symbols = BTreeSet::new();
2907    state_sync_symbols_inner(
2908        atn,
2909        state_number,
2910        stop_state,
2911        &mut BTreeSet::new(),
2912        &mut symbols,
2913    );
2914    symbols
2915}
2916
2917/// Walks epsilon-like continuations from a parent follow state until it finds
2918/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
2919fn state_sync_symbols_inner(
2920    atn: &Atn,
2921    state_number: usize,
2922    stop_state: usize,
2923    visited: &mut BTreeSet<usize>,
2924    symbols: &mut BTreeSet<i32>,
2925) {
2926    if !visited.insert(state_number) {
2927        return;
2928    }
2929    if state_number == stop_state {
2930        symbols.insert(TOKEN_EOF);
2931        return;
2932    }
2933    let Some(state) = atn.state(state_number) else {
2934        return;
2935    };
2936    for transition in &state.transitions() {
2937        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2938        if transition_symbols.is_empty() {
2939            match &transition.data() {
2940                Transition::Rule { target, .. }
2941                | Transition::Epsilon { target }
2942                | Transition::Action { target, .. }
2943                | Transition::Predicate { target, .. }
2944                | Transition::Precedence { target, .. } => {
2945                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
2946                }
2947                Transition::Atom { .. }
2948                | Transition::Range { .. }
2949                | Transition::Set { .. }
2950                | Transition::NotSet { .. }
2951                | Transition::Wildcard { .. } => {}
2952            }
2953        } else {
2954            symbols.extend(transition_symbols);
2955        }
2956    }
2957}
2958
2959#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2960struct OperatorSymbolReachability {
2961    /// One token completes an unconditional operator token-prefix.
2962    single_token: bool,
2963    /// An unconditional operator path requires more tokens before its operand.
2964    multi_token: bool,
2965    /// At least one matching operator path depends on a semantic predicate.
2966    predicate_dependent: bool,
2967}
2968
2969impl OperatorSymbolReachability {
2970    const ADAPTIVE_FALLBACK: Self = Self {
2971        single_token: false,
2972        multi_token: false,
2973        predicate_dependent: true,
2974    };
2975
2976    const fn single_token(predicate_dependent: bool) -> Self {
2977        if predicate_dependent {
2978            Self {
2979                single_token: false,
2980                multi_token: false,
2981                predicate_dependent: true,
2982            }
2983        } else {
2984            Self {
2985                single_token: true,
2986                multi_token: false,
2987                predicate_dependent: false,
2988            }
2989        }
2990    }
2991
2992    const fn multi_token(predicate_dependent: bool) -> Self {
2993        if predicate_dependent {
2994            Self {
2995                single_token: false,
2996                multi_token: false,
2997                predicate_dependent: true,
2998            }
2999        } else {
3000            Self {
3001                single_token: false,
3002                multi_token: true,
3003                predicate_dependent: false,
3004            }
3005        }
3006    }
3007
3008    const fn union(self, other: Self) -> Self {
3009        Self {
3010            single_token: self.single_token || other.single_token,
3011            multi_token: self.multi_token || other.multi_token,
3012            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3013        }
3014    }
3015}
3016
3017#[derive(Clone, Copy)]
3018struct OperatorReachabilityRequest {
3019    symbol: i32,
3020    precedence: i32,
3021    predicate_dependent: bool,
3022    operator_rule_index: usize,
3023}
3024
3025#[derive(Clone, Copy, Debug)]
3026struct OperatorRuleContinuation {
3027    stop_state: usize,
3028    follow_state: usize,
3029    return_precedence: i32,
3030}
3031
3032struct NullablePrecedenceCtx {
3033    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3034    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3035    hit_cycle: bool,
3036}
3037
3038fn state_is_nullable_with_precedence(
3039    atn: &Atn,
3040    state_number: usize,
3041    stop_state_number: usize,
3042    precedence: i32,
3043    allow_predicates: bool,
3044    ctx: &mut NullablePrecedenceCtx,
3045) -> bool {
3046    let saved_hit_cycle = ctx.hit_cycle;
3047    ctx.hit_cycle = false;
3048    let nullable = state_is_nullable_with_precedence_cached(
3049        atn,
3050        state_number,
3051        stop_state_number,
3052        precedence,
3053        allow_predicates,
3054        ctx,
3055    );
3056    ctx.hit_cycle = saved_hit_cycle;
3057    nullable
3058}
3059
3060fn state_is_nullable_with_precedence_cached(
3061    atn: &Atn,
3062    state_number: usize,
3063    stop_state_number: usize,
3064    precedence: i32,
3065    allow_predicates: bool,
3066    ctx: &mut NullablePrecedenceCtx,
3067) -> bool {
3068    if state_number == stop_state_number {
3069        return true;
3070    }
3071    let key = (
3072        state_number,
3073        stop_state_number,
3074        precedence,
3075        allow_predicates,
3076    );
3077    if let Some(cached) = ctx.cache.get(&key) {
3078        return *cached;
3079    }
3080    if !ctx.in_progress.insert(key) {
3081        ctx.hit_cycle = true;
3082        return false;
3083    }
3084    let saved_hit_cycle = ctx.hit_cycle;
3085    ctx.hit_cycle = false;
3086    let nullable = atn.state(state_number).is_some_and(|state| {
3087        state
3088            .transitions()
3089            .iter()
3090            .any(|transition| match &transition.data() {
3091                Transition::Rule {
3092                    target,
3093                    rule_index,
3094                    follow_state,
3095                    precedence: rule_precedence,
3096                } => {
3097                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3098                        return false;
3099                    };
3100                    state_is_nullable_with_precedence_cached(
3101                        atn,
3102                        *target,
3103                        child_stop,
3104                        *rule_precedence,
3105                        allow_predicates,
3106                        ctx,
3107                    ) && state_is_nullable_with_precedence_cached(
3108                        atn,
3109                        *follow_state,
3110                        stop_state_number,
3111                        precedence,
3112                        allow_predicates,
3113                        ctx,
3114                    )
3115                }
3116                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3117                    state_is_nullable_with_precedence_cached(
3118                        atn,
3119                        *target,
3120                        stop_state_number,
3121                        precedence,
3122                        allow_predicates,
3123                        ctx,
3124                    )
3125                }
3126                Transition::Predicate { target, .. } if allow_predicates => {
3127                    state_is_nullable_with_precedence_cached(
3128                        atn,
3129                        *target,
3130                        stop_state_number,
3131                        precedence,
3132                        allow_predicates,
3133                        ctx,
3134                    )
3135                }
3136                Transition::Precedence {
3137                    target,
3138                    precedence: transition_precedence,
3139                } if *transition_precedence >= precedence => {
3140                    state_is_nullable_with_precedence_cached(
3141                        atn,
3142                        *target,
3143                        stop_state_number,
3144                        precedence,
3145                        allow_predicates,
3146                        ctx,
3147                    )
3148                }
3149                Transition::Atom { .. }
3150                | Transition::Range { .. }
3151                | Transition::Set { .. }
3152                | Transition::NotSet { .. }
3153                | Transition::Wildcard { .. }
3154                | Transition::Predicate { .. }
3155                | Transition::Precedence { .. } => false,
3156            })
3157    });
3158    ctx.in_progress.remove(&key);
3159    if !ctx.hit_cycle {
3160        ctx.cache.insert(key, nullable);
3161    }
3162    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3163    nullable
3164}
3165
3166/// Classifies what remains after the operator's first token is matched.
3167fn state_operator_token_prefix_reachability(
3168    atn: &Atn,
3169    state_number: usize,
3170    request: OperatorReachabilityRequest,
3171    continuations: &[OperatorRuleContinuation],
3172    visited: &mut BTreeSet<(usize, i32, bool)>,
3173) -> OperatorSymbolReachability {
3174    let key = (
3175        state_number,
3176        request.precedence,
3177        request.predicate_dependent,
3178    );
3179    if !visited.insert(key) {
3180        // Recursive helper rules can grow the return stack without consuming
3181        // input. Delegate cycles to adaptive prediction instead of forcing a
3182        // potentially incomplete one-token answer.
3183        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3184    }
3185    if let Some((continuation, remaining)) = continuations.split_last()
3186        && state_number == continuation.stop_state
3187    {
3188        let result = state_operator_token_prefix_reachability(
3189            atn,
3190            continuation.follow_state,
3191            OperatorReachabilityRequest {
3192                precedence: continuation.return_precedence,
3193                ..request
3194            },
3195            remaining,
3196            visited,
3197        );
3198        visited.remove(&key);
3199        return result;
3200    }
3201    let Some(state) = atn.state(state_number) else {
3202        visited.remove(&key);
3203        return OperatorSymbolReachability::default();
3204    };
3205    let completes_operator = match state.kind() {
3206        AtnStateKind::RuleStop => continuations.is_empty(),
3207        AtnStateKind::StarLoopBack
3208        | AtnStateKind::StarLoopEntry
3209        | AtnStateKind::PlusLoopBack
3210        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3211        _ => false,
3212    };
3213    if completes_operator {
3214        visited.remove(&key);
3215        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3216    }
3217    let mut reachability = OperatorSymbolReachability::default();
3218    for transition in &state.transitions() {
3219        let transition_reachability = match &transition.data() {
3220            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3221                OperatorSymbolReachability::single_token(request.predicate_dependent)
3222            }
3223            Transition::Rule {
3224                target,
3225                rule_index,
3226                follow_state,
3227                precedence: rule_precedence,
3228            } => {
3229                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3230                    continue;
3231                };
3232                let mut nested = continuations.to_vec();
3233                nested.push(OperatorRuleContinuation {
3234                    stop_state: child_stop,
3235                    follow_state: *follow_state,
3236                    return_precedence: request.precedence,
3237                });
3238                state_operator_token_prefix_reachability(
3239                    atn,
3240                    *target,
3241                    OperatorReachabilityRequest {
3242                        precedence: *rule_precedence,
3243                        ..request
3244                    },
3245                    &nested,
3246                    visited,
3247                )
3248            }
3249            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3250                state_operator_token_prefix_reachability(
3251                    atn,
3252                    *target,
3253                    request,
3254                    continuations,
3255                    visited,
3256                )
3257            }
3258            Transition::Precedence {
3259                target,
3260                precedence: transition_precedence,
3261            } => {
3262                if *transition_precedence < request.precedence {
3263                    OperatorSymbolReachability::default()
3264                } else {
3265                    state_operator_token_prefix_reachability(
3266                        atn,
3267                        *target,
3268                        request,
3269                        continuations,
3270                        visited,
3271                    )
3272                }
3273            }
3274            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3275                atn,
3276                *target,
3277                OperatorReachabilityRequest {
3278                    predicate_dependent: true,
3279                    ..request
3280                },
3281                continuations,
3282                visited,
3283            ),
3284            Transition::Atom { .. }
3285            | Transition::Range { .. }
3286            | Transition::Set { .. }
3287            | Transition::NotSet { .. }
3288            | Transition::Wildcard { .. } => {
3289                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3290            }
3291        };
3292        reachability = reachability.union(transition_reachability);
3293    }
3294    visited.remove(&key);
3295    reachability
3296}
3297
3298fn state_can_reach_symbol_with_precedence(
3299    atn: &Atn,
3300    state_number: usize,
3301    request: OperatorReachabilityRequest,
3302    nullable_ctx: &mut NullablePrecedenceCtx,
3303    continuations: &mut Vec<OperatorRuleContinuation>,
3304    visited: &mut BTreeSet<(usize, i32, bool)>,
3305) -> OperatorSymbolReachability {
3306    let key = (
3307        state_number,
3308        request.precedence,
3309        request.predicate_dependent,
3310    );
3311    if !visited.insert(key) {
3312        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3313    }
3314    let Some(state) = atn.state(state_number) else {
3315        visited.remove(&key);
3316        return OperatorSymbolReachability::default();
3317    };
3318    let mut reachability = OperatorSymbolReachability::default();
3319    for transition in &state.transitions() {
3320        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3321            reachability = reachability.union(state_operator_token_prefix_reachability(
3322                atn,
3323                transition.target(),
3324                request,
3325                continuations,
3326                &mut BTreeSet::new(),
3327            ));
3328            continue;
3329        }
3330        let transition_reachability = match &transition.data() {
3331            Transition::Rule {
3332                target,
3333                rule_index,
3334                follow_state,
3335                precedence: rule_precedence,
3336            } => {
3337                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3338                    continue;
3339                };
3340                continuations.push(OperatorRuleContinuation {
3341                    stop_state: child_stop,
3342                    follow_state: *follow_state,
3343                    return_precedence: request.precedence,
3344                });
3345                let mut result = state_can_reach_symbol_with_precedence(
3346                    atn,
3347                    *target,
3348                    OperatorReachabilityRequest {
3349                        precedence: *rule_precedence,
3350                        ..request
3351                    },
3352                    nullable_ctx,
3353                    continuations,
3354                    visited,
3355                );
3356                continuations.pop();
3357                if state_is_nullable_with_precedence(
3358                    atn,
3359                    *target,
3360                    child_stop,
3361                    *rule_precedence,
3362                    true,
3363                    nullable_ctx,
3364                ) {
3365                    let child_predicate_dependent = request.predicate_dependent
3366                        || !state_is_nullable_with_precedence(
3367                            atn,
3368                            *target,
3369                            child_stop,
3370                            *rule_precedence,
3371                            false,
3372                            nullable_ctx,
3373                        );
3374                    result = result.union(state_can_reach_symbol_with_precedence(
3375                        atn,
3376                        *follow_state,
3377                        OperatorReachabilityRequest {
3378                            predicate_dependent: child_predicate_dependent,
3379                            ..request
3380                        },
3381                        nullable_ctx,
3382                        continuations,
3383                        visited,
3384                    ));
3385                }
3386                result
3387            }
3388            Transition::Epsilon { target }
3389            | Transition::Action { target, .. }
3390            | Transition::Precedence { target, .. } => {
3391                if matches!(
3392                    &transition.data(),
3393                    Transition::Precedence {
3394                        precedence: transition_precedence,
3395                        ..
3396                    } if *transition_precedence < request.precedence
3397                ) {
3398                    continue;
3399                }
3400                state_can_reach_symbol_with_precedence(
3401                    atn,
3402                    *target,
3403                    request,
3404                    nullable_ctx,
3405                    continuations,
3406                    visited,
3407                )
3408            }
3409            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3410                atn,
3411                *target,
3412                OperatorReachabilityRequest {
3413                    predicate_dependent: true,
3414                    ..request
3415                },
3416                nullable_ctx,
3417                continuations,
3418                visited,
3419            ),
3420            Transition::Atom { .. }
3421            | Transition::Range { .. }
3422            | Transition::Set { .. }
3423            | Transition::NotSet { .. }
3424            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3425        };
3426        reachability = reachability.union(transition_reachability);
3427    }
3428    visited.remove(&key);
3429    reachability
3430}
3431
3432fn left_recursive_operator_lookahead(
3433    atn: &Atn,
3434    state_number: usize,
3435    precedence: i32,
3436) -> LeftRecursiveOperatorLookahead {
3437    let Some(state) = atn.state(state_number) else {
3438        return LeftRecursiveOperatorLookahead::default();
3439    };
3440    let Some(operator_rule_index) = state.rule_index() else {
3441        return LeftRecursiveOperatorLookahead::default();
3442    };
3443    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3444    let mut nullable_ctx = NullablePrecedenceCtx {
3445        cache: FxHashMap::default(),
3446        in_progress: BTreeSet::new(),
3447        hit_cycle: false,
3448    };
3449    for transition in &state.transitions() {
3450        let target = transition.target();
3451        if atn
3452            .state(target)
3453            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3454        {
3455            continue;
3456        }
3457        for symbol in 1..=atn.max_token_type() {
3458            let reachability = state_can_reach_symbol_with_precedence(
3459                atn,
3460                target,
3461                OperatorReachabilityRequest {
3462                    symbol,
3463                    precedence,
3464                    predicate_dependent: false,
3465                    operator_rule_index,
3466                },
3467                &mut nullable_ctx,
3468                &mut Vec::new(),
3469                &mut BTreeSet::new(),
3470            );
3471            if reachability.single_token {
3472                lookahead.single_token.insert(symbol);
3473            }
3474            if reachability.multi_token {
3475                lookahead.multi_token_prefix.insert(symbol);
3476            }
3477            if reachability.predicate_dependent {
3478                lookahead.predicate_dependent.insert(symbol);
3479            }
3480        }
3481    }
3482    lookahead
3483}
3484
3485#[derive(Debug, Default)]
3486struct StateBeforeStopLookahead {
3487    symbols: TokenBitSet,
3488    reaches_context_boundary: bool,
3489}
3490
3491fn state_before_stop_lookahead(
3492    atn: &Atn,
3493    state_number: usize,
3494    stop_state_number: usize,
3495) -> Rc<StateBeforeStopLookahead> {
3496    with_shared_atn_caches(atn, |cache| {
3497        let key = (state_number, stop_state_number);
3498        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3499            return Rc::clone(cached);
3500        }
3501        let mut lookahead = StateBeforeStopLookahead::default();
3502        state_before_stop_lookahead_inner(
3503            atn,
3504            state_number,
3505            stop_state_number,
3506            &mut BTreeSet::new(),
3507            &mut cache.first_set,
3508            &mut lookahead,
3509        );
3510        let lookahead = Rc::new(lookahead);
3511        cache
3512            .state_before_stop_lookahead
3513            .insert(key, Rc::clone(&lookahead));
3514        lookahead
3515    })
3516}
3517
3518fn state_before_stop_lookahead_inner(
3519    atn: &Atn,
3520    state_number: usize,
3521    stop_state_number: usize,
3522    visited: &mut BTreeSet<usize>,
3523    first_set_cache: &mut FirstSetCache,
3524    lookahead: &mut StateBeforeStopLookahead,
3525) {
3526    if state_number == stop_state_number {
3527        lookahead.reaches_context_boundary = true;
3528        return;
3529    }
3530    if !visited.insert(state_number) {
3531        return;
3532    }
3533    let Some(state) = atn.state(state_number) else {
3534        return;
3535    };
3536    if state.kind() == AtnStateKind::RuleStop {
3537        lookahead.reaches_context_boundary = true;
3538        return;
3539    }
3540    for transition in &state.transitions() {
3541        match &transition.data() {
3542            Transition::Epsilon { target }
3543            | Transition::Action { target, .. }
3544            | Transition::Predicate { target, .. }
3545            | Transition::Precedence { target, .. } => {
3546                state_before_stop_lookahead_inner(
3547                    atn,
3548                    *target,
3549                    stop_state_number,
3550                    visited,
3551                    first_set_cache,
3552                    lookahead,
3553                );
3554            }
3555            Transition::Rule {
3556                target,
3557                rule_index,
3558                follow_state,
3559                ..
3560            } => {
3561                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3562                    continue;
3563                };
3564                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3565                lookahead.symbols.extend_from(&child.symbols);
3566                if child.nullable {
3567                    state_before_stop_lookahead_inner(
3568                        atn,
3569                        *follow_state,
3570                        stop_state_number,
3571                        visited,
3572                        first_set_cache,
3573                        lookahead,
3574                    );
3575                }
3576            }
3577            Transition::Atom { .. }
3578            | Transition::Range { .. }
3579            | Transition::Set { .. }
3580            | Transition::NotSet { .. }
3581            | Transition::Wildcard { .. } => {
3582                lookahead.symbols.extend_iter(transition_expected_symbols(
3583                    transition,
3584                    atn.max_token_type(),
3585                ));
3586            }
3587        }
3588    }
3589}
3590
3591fn caller_context_can_match_symbol_before_state(
3592    atn: &Atn,
3593    return_states: impl DoubleEndedIterator<Item = usize>,
3594    stop_state_number: usize,
3595    symbol: i32,
3596) -> bool {
3597    for return_state in return_states.rev() {
3598        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3599        if lookahead.symbols.contains(symbol) {
3600            return true;
3601        }
3602        if !lookahead.reaches_context_boundary {
3603            return false;
3604        }
3605    }
3606    false
3607}
3608
3609/// Carries recovery expectations and their restart state through epsilon-only
3610/// paths. ANTLR can report and repair at the decision state even when the
3611/// failed consuming transition is nested under block or loop epsilon edges.
3612fn next_recovery_context(
3613    atn: &Atn,
3614    state: AtnState<'_>,
3615    inherited: &BTreeSet<i32>,
3616    inherited_state: Option<usize>,
3617) -> (BTreeSet<i32>, Option<usize>) {
3618    let state_symbols = state_expected_symbols(atn, state.state_number());
3619    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3620        let mut symbols = state_symbols;
3621        symbols.extend(inherited.iter().copied());
3622        return (symbols, Some(state.state_number()));
3623    }
3624    (inherited.clone(), inherited_state)
3625}
3626
3627fn recovery_expected_symbols(
3628    atn: &Atn,
3629    state_number: usize,
3630    inherited: &BTreeSet<i32>,
3631) -> BTreeSet<i32> {
3632    let mut symbols = state_expected_symbols(atn, state_number);
3633    symbols.extend(inherited.iter().copied());
3634    symbols
3635}
3636
3637/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3638/// parser's cached state-expected-symbols sets and the inherited `Rc`
3639/// without copying when the state cannot widen recovery.
3640fn fast_next_recovery_context<S, H>(
3641    parser: &mut BaseParser<S, H>,
3642    atn: &Atn,
3643    state: AtnState<'_>,
3644    inherited: &Rc<BTreeSet<i32>>,
3645    inherited_state: Option<usize>,
3646) -> (Rc<BTreeSet<i32>>, Option<usize>)
3647where
3648    S: TokenSource,
3649    H: SemanticHooks,
3650{
3651    if state.transitions().len() <= 1 {
3652        return (Rc::clone(inherited), inherited_state);
3653    }
3654    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3655    if state_symbols.is_empty() {
3656        return (Rc::clone(inherited), inherited_state);
3657    }
3658    if inherited.is_empty() {
3659        return (state_symbols, Some(state.state_number()));
3660    }
3661    if Rc::ptr_eq(&state_symbols, inherited) {
3662        return (state_symbols, Some(state.state_number()));
3663    }
3664    let mut combined = (*state_symbols).clone();
3665    combined.extend(inherited.iter().copied());
3666    (
3667        parser.intern_recovery_symbols(combined),
3668        Some(state.state_number()),
3669    )
3670}
3671
3672/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3673/// cached state-expected-symbols and avoids cloning when no widening is
3674/// needed.
3675fn fast_recovery_expected_symbols<S, H>(
3676    parser: &mut BaseParser<S, H>,
3677    atn: &Atn,
3678    state_number: usize,
3679    inherited: &Rc<BTreeSet<i32>>,
3680) -> Rc<BTreeSet<i32>>
3681where
3682    S: TokenSource,
3683    H: SemanticHooks,
3684{
3685    let cached = parser.cached_state_expected_symbols(atn, state_number);
3686    if inherited.is_empty() {
3687        return cached;
3688    }
3689    if cached.is_empty() {
3690        return Rc::clone(inherited);
3691    }
3692    if Rc::ptr_eq(&cached, inherited) {
3693        return cached;
3694    }
3695    let mut combined = (*cached).clone();
3696    combined.extend(inherited.iter().copied());
3697    parser.intern_recovery_symbols(combined)
3698}
3699
3700struct ParserTableSemCtx<'a> {
3701    member_values: &'a mut BTreeMap<usize, i64>,
3702    return_values: &'a mut BTreeMap<String, i64>,
3703}
3704
3705impl semir::PredContext for ParserTableSemCtx<'_> {
3706    type TokenText<'a>
3707        = &'a str
3708    where
3709        Self: 'a;
3710
3711    fn la(&mut self, _offset: isize) -> i64 {
3712        i64::from(TOKEN_EOF)
3713    }
3714
3715    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3716        None
3717    }
3718
3719    fn token_index_adjacent(&mut self) -> bool {
3720        false
3721    }
3722
3723    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3724        None
3725    }
3726
3727    fn member(&self, member: usize) -> Option<i64> {
3728        Some(self.member_values.get(&member).copied().unwrap_or_default())
3729    }
3730
3731    fn local_arg(&self) -> Option<i64> {
3732        None
3733    }
3734
3735    fn column(&self) -> Option<i64> {
3736        None
3737    }
3738
3739    fn token_start_column(&self) -> Option<i64> {
3740        None
3741    }
3742
3743    fn token_text_so_far(&self) -> Option<String> {
3744        None
3745    }
3746
3747    fn hook(&mut self, _hook: HookId) -> bool {
3748        false
3749    }
3750}
3751
3752impl semir::ActContext for ParserTableSemCtx<'_> {
3753    fn set_member(&mut self, member: usize, value: i64) {
3754        self.member_values.insert(member, value);
3755    }
3756
3757    fn set_return(&mut self, name: &str, value: i64) {
3758        self.return_values.insert(name.to_owned(), value);
3759    }
3760
3761    fn action_hook(&mut self, _hook: HookId) {}
3762}
3763
3764/// Applies generated integer-member side effects to one speculative path.
3765fn apply_member_actions(
3766    source_state: usize,
3767    actions: &[ParserMemberAction],
3768    semantics: Option<&ParserSemantics>,
3769    values: &mut BTreeMap<usize, i64>,
3770) {
3771    for action in actions
3772        .iter()
3773        .filter(|action| action.source_state == source_state)
3774    {
3775        *values.entry(action.member).or_default() += action.delta;
3776    }
3777    let Some(semantics) = semantics else {
3778        return;
3779    };
3780    let mut return_values = BTreeMap::new();
3781    let mut ctx = ParserTableSemCtx {
3782        member_values: values,
3783        return_values: &mut return_values,
3784    };
3785    for action in semantics
3786        .actions
3787        .iter()
3788        .filter(|action| action.source_state == source_state && action.speculative)
3789    {
3790        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3791    }
3792}
3793
3794/// Returns the speculative member state after replaying one ATN action state.
3795fn member_values_after_action(
3796    source_state: usize,
3797    actions: &[ParserMemberAction],
3798    semantics: Option<&ParserSemantics>,
3799    values: &BTreeMap<usize, i64>,
3800) -> BTreeMap<usize, i64> {
3801    let mut values = values.clone();
3802    apply_member_actions(source_state, actions, semantics, &mut values);
3803    values
3804}
3805
3806/// Returns the speculative rule-return state after replaying one ATN action.
3807fn return_values_after_action(
3808    source_state: usize,
3809    rule_index: usize,
3810    actions: &[ParserReturnAction],
3811    semantics: Option<&ParserSemantics>,
3812    values: &BTreeMap<String, i64>,
3813) -> BTreeMap<String, i64> {
3814    let mut values = values.clone();
3815    for action in actions
3816        .iter()
3817        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
3818    {
3819        values.insert(action.name.to_owned(), action.value);
3820    }
3821    if let Some(semantics) = semantics {
3822        let mut member_values = BTreeMap::new();
3823        let mut ctx = ParserTableSemCtx {
3824            member_values: &mut member_values,
3825            return_values: &mut values,
3826        };
3827        for action in semantics.actions.iter().filter(|action| {
3828            action.source_state == source_state
3829                && action.rule_index == rule_index
3830                && !action.speculative
3831        }) {
3832            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
3833        }
3834    }
3835    values
3836}
3837
3838/// Resolves the integer argument visible to a child rule invocation.
3839fn rule_local_int_arg(
3840    rule_args: &[ParserRuleArg],
3841    source_state: usize,
3842    rule_index: usize,
3843    local_int_arg: Option<(usize, i64)>,
3844) -> Option<(usize, i64)> {
3845    rule_args
3846        .iter()
3847        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
3848        .map(|arg| {
3849            let value = if arg.inherit_local {
3850                local_int_arg.map_or(arg.value, |(_, value)| value)
3851            } else {
3852                arg.value
3853            };
3854            (rule_index, value)
3855        })
3856}
3857
3858/// Builds the terminal recognition outcome for a path that reached its stop
3859/// state.
3860fn stop_outcome(
3861    index: usize,
3862    consumed_eof: bool,
3863    rule_alt_number: usize,
3864    member_values: BTreeMap<usize, i64>,
3865    return_values: BTreeMap<String, i64>,
3866) -> Vec<RecognizeOutcome> {
3867    vec![RecognizeOutcome {
3868        index,
3869        consumed_eof,
3870        alt_number: rule_alt_number,
3871        member_values,
3872        return_values,
3873        diagnostics: DiagnosticSeqId::EMPTY,
3874        decisions: Vec::new(),
3875        actions: Vec::new(),
3876        nodes: NodeSeqId::EMPTY,
3877    }]
3878}
3879
3880fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
3881    with_shared_atn_caches(atn, |cache| {
3882        *cache.observable_action_transitions.get_or_insert_with(|| {
3883            atn.states().any(|state| {
3884                state.transitions().iter().any(|transition| {
3885                    matches!(
3886                        &transition.data(),
3887                        Transition::Action {
3888                            action_index: Some(_),
3889                            ..
3890                        }
3891                    )
3892                })
3893            })
3894        })
3895    })
3896}
3897
3898fn atn_has_predicate_transitions(atn: &Atn) -> bool {
3899    with_shared_atn_caches(atn, |cache| {
3900        *cache.predicate_transitions.get_or_insert_with(|| {
3901            atn.states().any(|state| {
3902                state
3903                    .transitions()
3904                    .iter()
3905                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
3906            })
3907        })
3908    })
3909}
3910
3911/// Reports whether predicates are the only observable semantics the fast
3912/// recognizer must preserve. Without path-local actions, arguments, or return
3913/// state, repeated evaluation at one coordinate and input index receives the
3914/// same runtime context.
3915fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
3916    options.init_action_rules.is_empty()
3917        && !options.track_alt_numbers
3918        && options
3919            .predicates
3920            .iter()
3921            .all(|(_, _, predicate)| predicate.failure_message().is_none())
3922        && options.semantics.is_none_or(|semantics| {
3923            semantics.actions.is_empty()
3924                && semantics
3925                    .predicates
3926                    .iter()
3927                    .all(|predicate| predicate.failure_message.is_none())
3928        })
3929        && options.rule_args.is_empty()
3930        && options.member_actions.is_empty()
3931        && options.return_actions.is_empty()
3932        && !atn_has_observable_action_transitions(atn)
3933}
3934
3935#[derive(Clone, Debug, Eq, PartialEq)]
3936struct RecognizeRequest<'a> {
3937    state_number: usize,
3938    stop_state: usize,
3939    index: usize,
3940    rule_start_index: usize,
3941    decision_start_index: Option<usize>,
3942    init_action_rules: &'a BTreeSet<usize>,
3943    predicates: &'a [(usize, usize, ParserPredicate)],
3944    semantics: Option<&'a ParserSemantics>,
3945    rule_args: &'a [ParserRuleArg],
3946    member_actions: &'a [ParserMemberAction],
3947    return_actions: &'a [ParserReturnAction],
3948    local_int_arg: Option<(usize, i64)>,
3949    member_values: BTreeMap<usize, i64>,
3950    return_values: BTreeMap<String, i64>,
3951    rule_alt_number: usize,
3952    track_alt_numbers: bool,
3953    consumed_eof: bool,
3954    /// Current left-recursive precedence threshold, matching ANTLR's
3955    /// `precpred(_ctx, k)` check for generated precedence rules.
3956    precedence: i32,
3957    depth: usize,
3958    recovery_symbols: BTreeSet<i32>,
3959    recovery_state: Option<usize>,
3960}
3961
3962#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
3963struct RecognizeKey {
3964    state_number: usize,
3965    stop_state: usize,
3966    index: usize,
3967    rule_start_index: usize,
3968    decision_start_index: Option<usize>,
3969    local_int_arg: Option<(usize, i64)>,
3970    member_values: BTreeMap<usize, i64>,
3971    return_values: BTreeMap<String, i64>,
3972    rule_alt_number: usize,
3973    track_alt_numbers: bool,
3974    consumed_eof: bool,
3975    precedence: i32,
3976    recovery_symbols: BTreeSet<i32>,
3977    recovery_state: Option<usize>,
3978}
3979
3980#[derive(Clone, Debug, Eq, PartialEq)]
3981struct EpsilonActionStep {
3982    source_state: usize,
3983    target: usize,
3984    action_rule_index: Option<usize>,
3985    left_recursive_boundary: Option<usize>,
3986    decision: Option<usize>,
3987    decision_start_index: Option<usize>,
3988    alt_number: usize,
3989    recovery_symbols: BTreeSet<i32>,
3990    recovery_state: Option<usize>,
3991}
3992
3993struct RecognizeScratch<'a> {
3994    visiting: &'a mut BTreeSet<RecognizeKey>,
3995    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
3996    expected: &'a mut ExpectedTokens,
3997}
3998
3999#[derive(Clone, Debug, Eq, PartialEq)]
4000struct FastRecognizeRequest {
4001    state_number: usize,
4002    stop_state: usize,
4003    index: usize,
4004    rule_start_index: usize,
4005    decision_start_index: Option<usize>,
4006    precedence: i32,
4007    depth: usize,
4008    recovery_symbols: Rc<BTreeSet<i32>>,
4009    recovery_state: Option<usize>,
4010}
4011
4012#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4013struct FastRecognizeTopRequest {
4014    start_state: usize,
4015    stop_state: usize,
4016    start_index: usize,
4017    precedence: i32,
4018    caller_follow_state: Option<usize>,
4019}
4020
4021#[derive(Clone, Copy, Debug)]
4022struct FastPredicateContext<'a> {
4023    predicates: &'a [(usize, usize, ParserPredicate)],
4024    semantics: Option<&'a ParserSemantics>,
4025    member_values: &'a BTreeMap<usize, i64>,
4026}
4027
4028struct FastRecognizeScratch<'a, 'b> {
4029    predicate_context: Option<FastPredicateContext<'a>>,
4030    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4031    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4032    expected: &'b mut ExpectedTokens,
4033}
4034
4035#[derive(Clone, Copy, Debug)]
4036struct FastRepetitionShape {
4037    enter_target: usize,
4038    exit_target: usize,
4039    body_stop_state: usize,
4040    enter_transition_index: usize,
4041    exit_transition_index: usize,
4042}
4043
4044#[derive(Clone, Copy, Debug)]
4045struct FastRepetitionPath {
4046    index: usize,
4047    deferred_nodes: FastDeferredNodeId,
4048    diagnostics: DiagnosticSeqId,
4049    consumed_eof: bool,
4050}
4051
4052enum FastRepetitionWork {
4053    Enter(FastRepetitionPath),
4054    Exit(FastRepetitionPath),
4055}
4056
4057/// Dense entered/exited coordinate sets for one repetition walk.
4058///
4059/// The start coordinate stays inline so short loops avoid a heap allocation;
4060/// later token indexes use one byte each instead of two hash-table entries.
4061struct FastRepetitionCoordinates {
4062    base_index: usize,
4063    base_state: u8,
4064    later_states: Vec<u8>,
4065}
4066
4067impl FastRepetitionCoordinates {
4068    const ENTERED: u8 = 0;
4069    const EXITED: u8 = 2;
4070
4071    const fn new(base_index: usize) -> Self {
4072        Self {
4073            base_index,
4074            base_state: 0,
4075            later_states: Vec::new(),
4076        }
4077    }
4078
4079    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4080        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4081    }
4082
4083    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4084        self.insert(path.index, path.consumed_eof, Self::EXITED)
4085    }
4086
4087    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4088        let Some(offset) = index.checked_sub(self.base_index) else {
4089            return false;
4090        };
4091        let state = if offset == 0 {
4092            &mut self.base_state
4093        } else {
4094            if self.later_states.len() < offset {
4095                self.later_states.resize(offset, 0);
4096            }
4097            &mut self.later_states[offset - 1]
4098        };
4099        let bit = 1 << (base_bit + u8::from(consumed_eof));
4100        let is_new = *state & bit == 0;
4101        *state |= bit;
4102        is_new
4103    }
4104}
4105
4106fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4107    if state.precedence_rule_decision()
4108        || !matches!(
4109            state.kind(),
4110            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4111        )
4112        || state.transitions().len() != 2
4113    {
4114        return None;
4115    }
4116    let mut enter = None;
4117    let mut exit = None;
4118    for (index, transition) in state.transitions().iter().enumerate() {
4119        if transition.kind() != ParserTransitionKind::Epsilon {
4120            return None;
4121        }
4122        let target = transition.target();
4123        if atn
4124            .state(target)
4125            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4126        {
4127            if exit.replace((index, target)).is_some() {
4128                return None;
4129            }
4130        } else if enter.replace((index, target)).is_some() {
4131            return None;
4132        }
4133    }
4134    let (enter_transition_index, enter_target) = enter?;
4135    let (exit_transition_index, exit_target) = exit?;
4136    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4137        atn.state(exit_target)?.loop_back_state()?
4138    } else {
4139        state.state_number()
4140    };
4141    Some(FastRepetitionShape {
4142        enter_target,
4143        exit_target,
4144        body_stop_state,
4145        enter_transition_index,
4146        exit_transition_index,
4147    })
4148}
4149
4150fn push_fast_repetition_work(
4151    work: &mut Vec<FastRepetitionWork>,
4152    shape: FastRepetitionShape,
4153    path: FastRepetitionPath,
4154    lookahead: Option<&DecisionLookahead>,
4155    symbol: i32,
4156) {
4157    // Match the normal recognizer's FIRST-set pruning before queueing work.
4158    // Ambiguous body paths still share the coordinate bitmap below.
4159    let transition_is_viable = |transition_index: usize| {
4160        let Some(entry) = lookahead else {
4161            return true;
4162        };
4163        let Some(transition) = entry.transitions.get(transition_index) else {
4164            return true;
4165        };
4166        transition.nullable || transition.symbols.contains(symbol)
4167    };
4168    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4169    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4170    if shape.enter_transition_index < shape.exit_transition_index {
4171        if exit_is_viable {
4172            work.push(FastRepetitionWork::Exit(path));
4173        }
4174        if enter_is_viable {
4175            work.push(FastRepetitionWork::Enter(path));
4176        }
4177    } else {
4178        if enter_is_viable {
4179            work.push(FastRepetitionWork::Enter(path));
4180        }
4181        if exit_is_viable {
4182            work.push(FastRepetitionWork::Exit(path));
4183        }
4184    }
4185}
4186
4187/// Memo key for the fast recognizer. `recovery_symbols` must come from
4188/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4189/// key, so equal sets share one allocation and the key can store that
4190/// allocation's address instead of cloning an `Rc` and walking the full
4191/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4192/// into distinct cache coordinates.
4193#[derive(Clone, Debug)]
4194struct FastRecognizeKey {
4195    state_number: usize,
4196    stop_state: usize,
4197    index: usize,
4198    rule_start_index: usize,
4199    decision_start_index: Option<usize>,
4200    precedence: i32,
4201    recovery_symbols_id: usize,
4202    recovery_state: Option<usize>,
4203}
4204
4205impl PartialEq for FastRecognizeKey {
4206    fn eq(&self, other: &Self) -> bool {
4207        if self.state_number != other.state_number
4208            || self.stop_state != other.stop_state
4209            || self.index != other.index
4210            || self.rule_start_index != other.rule_start_index
4211            || self.decision_start_index != other.decision_start_index
4212            || self.precedence != other.precedence
4213            || self.recovery_state != other.recovery_state
4214            || self.recovery_symbols_id != other.recovery_symbols_id
4215        {
4216            return false;
4217        }
4218        true
4219    }
4220}
4221
4222impl Eq for FastRecognizeKey {}
4223
4224impl Hash for FastRecognizeKey {
4225    fn hash<H: Hasher>(&self, hasher: &mut H) {
4226        self.state_number.hash(hasher);
4227        self.stop_state.hash(hasher);
4228        self.index.hash(hasher);
4229        self.rule_start_index.hash(hasher);
4230        self.decision_start_index.hash(hasher);
4231        self.precedence.hash(hasher);
4232        self.recovery_state.hash(hasher);
4233        self.recovery_symbols_id.hash(hasher);
4234    }
4235}
4236
4237struct FastRecoveryRequest<'a, 'b> {
4238    atn: &'a Atn,
4239    transition: ParserTransition<'a>,
4240    expected_symbols: Rc<BTreeSet<i32>>,
4241    target: usize,
4242    request: FastRecognizeRequest,
4243    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4244    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4245    expected: &'b mut ExpectedTokens,
4246}
4247
4248struct FastCurrentTokenDeletionRequest<'a, 'b> {
4249    atn: &'a Atn,
4250    expected_symbols: Rc<BTreeSet<i32>>,
4251    request: FastRecognizeRequest,
4252    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4253    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4254    expected: &'b mut ExpectedTokens,
4255}
4256
4257#[derive(Clone, Copy)]
4258struct FastChildRuleFailureRecoveryRequest<'a> {
4259    atn: &'a Atn,
4260    rule_index: usize,
4261    start_index: usize,
4262    follow_state: usize,
4263    stop_state: usize,
4264    expected: &'a ExpectedTokens,
4265}
4266
4267struct RecoveryRequest<'a, 'b> {
4268    atn: &'a Atn,
4269    transition: ParserTransition<'a>,
4270    expected_symbols: BTreeSet<i32>,
4271    target: usize,
4272    request: RecognizeRequest<'a>,
4273    visiting: &'b mut BTreeSet<RecognizeKey>,
4274    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4275    expected: &'b mut ExpectedTokens,
4276}
4277
4278struct CurrentTokenDeletionRequest<'a, 'b> {
4279    atn: &'a Atn,
4280    expected_symbols: BTreeSet<i32>,
4281    request: RecognizeRequest<'a>,
4282    visiting: &'b mut BTreeSet<RecognizeKey>,
4283    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4284    expected: &'b mut ExpectedTokens,
4285}
4286
4287/// Carries the state needed after the normal token-recovery strategies fail
4288/// for a consuming transition.
4289struct ConsumingFailureFallback<'a> {
4290    atn: &'a Atn,
4291    target: usize,
4292    request: RecognizeRequest<'a>,
4293    symbol: i32,
4294    expected_symbols: BTreeSet<i32>,
4295    decision_start_index: Option<usize>,
4296    decision: Option<usize>,
4297}
4298
4299/// Captures the parent-rule context needed when a called rule fails before it
4300/// can produce a normal outcome.
4301struct ChildRuleFailureRecovery<'a> {
4302    atn: &'a Atn,
4303    rule_index: usize,
4304    start_index: usize,
4305    follow_state: usize,
4306    stop_state: usize,
4307    member_values: BTreeMap<usize, i64>,
4308    expected: &'a ExpectedTokens,
4309}
4310
4311/// Bundles the context needed to evaluate one semantic predicate transition.
4312#[derive(Clone, Copy, Debug)]
4313struct PredicateEval<'a> {
4314    index: usize,
4315    rule_index: usize,
4316    pred_index: usize,
4317    predicates: &'a [(usize, usize, ParserPredicate)],
4318    semantics: Option<&'a ParserSemantics>,
4319    context: Option<&'a ParserRuleContext>,
4320    local_int_arg: Option<(usize, i64)>,
4321    member_values: &'a BTreeMap<usize, i64>,
4322}
4323
4324#[derive(Clone, Copy, Debug)]
4325struct ParserSemanticHookRequest<'a> {
4326    index: usize,
4327    rule_index: usize,
4328    pred_index: usize,
4329    context: Option<&'a ParserRuleContext>,
4330    local_int_arg: Option<(usize, i64)>,
4331    member_values: &'a BTreeMap<usize, i64>,
4332}
4333
4334/// Predicate-evaluation context over the recognizer's speculative state.
4335///
4336/// This sits in the prediction hot loop, so everything is borrowed: member
4337/// state read-only from the current speculative path and the rule name
4338/// straight from recognizer metadata. Predicates are pure by construction
4339/// ([`semir::PExpr`] has no mutating node); statement execution uses
4340/// [`ParserTableSemCtx`] (speculative member/return replay) and
4341/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4342struct ParserSemIrCtx<'a, S, H>
4343where
4344    S: TokenSource,
4345    H: SemanticHooks,
4346{
4347    input: &'a mut CommonTokenStream<S>,
4348    tree_storage: &'a ParseTreeStorage,
4349    semantic_hooks: &'a mut H,
4350    rule_index: usize,
4351    coordinate_index: usize,
4352    rule_name: Option<&'a str>,
4353    context: Option<&'a ParserRuleContext>,
4354    local_int_arg: Option<(usize, i64)>,
4355    member_values: &'a BTreeMap<usize, i64>,
4356    invoked_predicates: &'a mut Vec<(usize, usize)>,
4357    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4358    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4359    /// table path instead of coercing the miss to `false`.
4360    unknown_predicate_policy: UnknownSemanticPolicy,
4361    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4362}
4363
4364impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4365where
4366    S: TokenSource,
4367    H: SemanticHooks,
4368{
4369    type TokenText<'a>
4370        = TokenView<'a>
4371    where
4372        Self: 'a;
4373
4374    fn la(&mut self, offset: isize) -> i64 {
4375        i64::from(self.input.la(offset))
4376    }
4377
4378    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4379        self.input.lt(offset)
4380    }
4381
4382    fn token_index_adjacent(&mut self) -> bool {
4383        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4384            return false;
4385        };
4386        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4387            return false;
4388        };
4389        first + 1 == second
4390    }
4391
4392    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4393        self.context.and_then(|context| {
4394            context
4395                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4396                .next()
4397                .map(crate::tree::RuleNodeView::text)
4398        })
4399    }
4400
4401    fn member(&self, member: usize) -> Option<i64> {
4402        Some(self.member_values.get(&member).copied().unwrap_or_default())
4403    }
4404
4405    fn local_arg(&self) -> Option<i64> {
4406        self.local_int_arg.map(|(_, value)| value)
4407    }
4408
4409    fn column(&self) -> Option<i64> {
4410        None
4411    }
4412
4413    fn token_start_column(&self) -> Option<i64> {
4414        None
4415    }
4416
4417    fn token_text_so_far(&self) -> Option<String> {
4418        None
4419    }
4420
4421    fn hook(&mut self, _hook: HookId) -> bool {
4422        let mut ctx = ParserSemCtx {
4423            input: &mut *self.input,
4424            tree_storage: self.tree_storage,
4425            rule_index: self.rule_index,
4426            coordinate_index: self.coordinate_index,
4427            rule_name: self.rule_name.map(str::to_owned),
4428            context: self.context,
4429            tree: None,
4430            local_int_arg: self.local_int_arg,
4431            member_values: self.member_values,
4432            action: None,
4433        };
4434        match self
4435            .semantic_hooks
4436            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4437        {
4438            Some(result) => result,
4439            // No hook answered this coordinate: fall through to the configured
4440            // policy instead of silently rejecting the alternative, matching the
4441            // legacy table path's dispatch chain (hook → policy).
4442            None => apply_unknown_predicate_policy(
4443                self.unknown_predicate_policy,
4444                self.rule_index,
4445                self.coordinate_index,
4446                self.unknown_predicate_hits,
4447            ),
4448        }
4449    }
4450
4451    fn trace_bool(&mut self, value: bool) -> bool {
4452        let key = (self.rule_index, self.coordinate_index);
4453        if !self.invoked_predicates.contains(&key) {
4454            self.invoked_predicates.push(key);
4455            use std::io::Write as _;
4456            let mut stdout = std::io::stdout().lock();
4457            let _ = writeln!(stdout, "eval={value}");
4458        }
4459        value
4460    }
4461}
4462
4463/// Captures predicate-failure recovery metadata for fail-option predicates.
4464struct PredicateFailureRecovery<'a> {
4465    rule_index: usize,
4466    index: usize,
4467    message: &'a str,
4468    member_values: BTreeMap<usize, i64>,
4469    return_values: BTreeMap<String, i64>,
4470    rule_alt_number: usize,
4471}
4472
4473#[derive(Debug)]
4474enum DirectAdaptiveParseControl {
4475    Fallback(DirectAdaptiveFallback),
4476}
4477
4478#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4479enum DirectAdaptiveFallback {
4480    Action,
4481    InvalidAlt,
4482    LeftRecursiveBoundary,
4483    MissingAtn,
4484    NoTransition,
4485    Predicate,
4486    Prediction,
4487    Precedence,
4488    RuleStop,
4489    SemanticContext,
4490    StepLimit,
4491    TokenMismatch,
4492    UnknownDecision,
4493}
4494
4495type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4496
4497struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4498where
4499    S: TokenSource,
4500    H: SemanticHooks,
4501{
4502    parser: &'sim mut BaseParser<S, H>,
4503    atn: &'atn Atn,
4504    simulator: &'sim mut ParserAtnSimulator<'atn>,
4505    decision_by_state: Vec<Option<usize>>,
4506    steps: usize,
4507}
4508
4509/// Outcome of a generated token / set / not-set match that may recover.
4510///
4511/// Generated parsers append `children` to the current rule context. `consumed_eof`
4512/// reports whether the match actually consumed a real EOF terminal — it is true
4513/// only on a successful match (or single-token deletion that lands on EOF), and
4514/// always false on single-token insertion, which synthesizes a missing token and
4515/// consumes nothing. Generated code feeds this into `finish_rule`'s
4516/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4517/// truly matched, matching ANTLR's `matchedEOF` semantics.
4518#[derive(Clone, Debug, Eq, PartialEq)]
4519pub struct GeneratedMatch {
4520    children: GeneratedMatchChildren,
4521    consumed_eof: bool,
4522}
4523
4524#[derive(Clone, Copy)]
4525enum GeneratedExpectedSymbols<'a> {
4526    Tree(&'a BTreeSet<i32>),
4527    TokenSet(ParserIntervalSet<'a>),
4528    TokenSetComplement {
4529        set: ParserIntervalSet<'a>,
4530        min_vocabulary: i32,
4531        max_vocabulary: i32,
4532    },
4533}
4534
4535impl GeneratedExpectedSymbols<'_> {
4536    fn is_empty(self) -> bool {
4537        match self {
4538            Self::Tree(symbols) => symbols.is_empty(),
4539            Self::TokenSet(set) => set.is_empty(),
4540            Self::TokenSetComplement {
4541                set,
4542                min_vocabulary,
4543                max_vocabulary,
4544            } => (min_vocabulary..=max_vocabulary).all(|symbol| set.contains(symbol)),
4545        }
4546    }
4547
4548    fn first(self) -> Option<i32> {
4549        match self {
4550            Self::Tree(symbols) => symbols.iter().next().copied(),
4551            Self::TokenSet(set) => set.ranges().next().map(|(start, _)| start),
4552            Self::TokenSetComplement {
4553                set,
4554                min_vocabulary,
4555                max_vocabulary,
4556            } => (min_vocabulary..=max_vocabulary).find(|symbol| !set.contains(*symbol)),
4557        }
4558    }
4559
4560    fn display(self, vocabulary: &Vocabulary) -> String {
4561        match self {
4562            Self::Tree(symbols) => expected_symbols_display(symbols, vocabulary),
4563            Self::TokenSet(set) => expected_symbols_display_iter(
4564                set.ranges().flat_map(|(start, stop)| start..=stop),
4565                vocabulary,
4566            ),
4567            Self::TokenSetComplement {
4568                set,
4569                min_vocabulary,
4570                max_vocabulary,
4571            } => expected_symbols_display_iter(
4572                (min_vocabulary..=max_vocabulary).filter(|symbol| !set.contains(*symbol)),
4573                vocabulary,
4574            ),
4575        }
4576    }
4577}
4578
4579#[derive(Clone, Debug, Eq, PartialEq)]
4580enum GeneratedMatchChildren {
4581    One(ParseTree),
4582    Many(Vec<ParseTree>),
4583}
4584
4585struct GeneratedMatchChildrenIntoIter {
4586    one: Option<ParseTree>,
4587    many: Option<std::vec::IntoIter<ParseTree>>,
4588}
4589
4590impl Iterator for GeneratedMatchChildrenIntoIter {
4591    type Item = ParseTree;
4592
4593    fn next(&mut self) -> Option<Self::Item> {
4594        self.one
4595            .take()
4596            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4597    }
4598}
4599
4600impl GeneratedMatch {
4601    /// Parse-tree children produced by the match (the matched terminal, an
4602    /// error node plus deleted-then-matched terminal, or a single missing-token
4603    /// error node).
4604    #[must_use]
4605    pub fn children(&self) -> &[ParseTree] {
4606        match &self.children {
4607            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4608            GeneratedMatchChildren::Many(children) => children,
4609        }
4610    }
4611
4612    /// Consumes the result, returning the children for appending to the rule
4613    /// context.
4614    #[must_use]
4615    pub fn into_children(self) -> Vec<ParseTree> {
4616        match self.children {
4617            GeneratedMatchChildren::One(child) => vec![child],
4618            GeneratedMatchChildren::Many(children) => children,
4619        }
4620    }
4621
4622    /// Consumes the match without allocating for the common single-child case.
4623    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4624        match self.children {
4625            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4626                one: Some(child),
4627                many: None,
4628            },
4629            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4630                one: None,
4631                many: Some(children.into_iter()),
4632            },
4633        }
4634    }
4635
4636    /// Whether a real EOF terminal was consumed by this match.
4637    #[must_use]
4638    pub const fn consumed_eof(&self) -> bool {
4639        self.consumed_eof
4640    }
4641}
4642
4643impl<S> BaseParser<S, NoSemanticHooks>
4644where
4645    S: TokenSource,
4646{
4647    /// Creates a parser base over a buffered token stream and recognizer
4648    /// metadata.
4649    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4650        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4651    }
4652}
4653
4654impl<S, H> BaseParser<S, H>
4655where
4656    S: TokenSource,
4657    H: SemanticHooks,
4658{
4659    /// Creates a parser base with caller-owned semantic hooks.
4660    pub fn with_semantic_hooks(
4661        input: CommonTokenStream<S>,
4662        data: RecognizerData,
4663        semantic_hooks: H,
4664    ) -> Self {
4665        Self {
4666            input,
4667            tree: ParseTreeStorage::new(),
4668            data,
4669            semantic_hooks,
4670            build_parse_trees: true,
4671            syntax_errors: 0,
4672            report_diagnostic_errors: false,
4673            prediction_mode: PredictionMode::Ll,
4674            prediction_diagnostics: Vec::new(),
4675            reported_prediction_diagnostics: BTreeSet::new(),
4676            generated_parser_diagnostics: Vec::new(),
4677            generated_sync_expected: None,
4678            int_members: BTreeMap::new(),
4679            rule_context_stack: Vec::new(),
4680            rule_context_version: 0,
4681            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4682            pending_invoking_states: Vec::new(),
4683            precedence_stack: vec![0],
4684            invoked_predicates: Vec::new(),
4685            bail_on_error: false,
4686            unknown_predicate_policy: UnknownSemanticPolicy::default(),
4687            unknown_predicate_hits: Vec::new(),
4688            unhandled_action_hits: Vec::new(),
4689            rule_first_set_cache: Vec::new(),
4690            state_expected_cache: FxHashMap::default(),
4691            state_expected_token_cache: FxHashMap::default(),
4692            rule_stop_reach_cache: Vec::new(),
4693            recovery_symbols_intern: FxHashMap::default(),
4694            decision_lookahead_cache: FxHashMap::default(),
4695            ll1_decision_cache: FxHashMap::default(),
4696            fast_predicate_cache: FxHashMap::default(),
4697            empty_cycle_cache: Vec::new(),
4698            empty_cycle_cache_atn: None,
4699            clean_memo_mode: CleanMemoMode::Probe,
4700            clean_memo_probe_seen: FxHashSet::default(),
4701            clean_memo_probe_samples: 0,
4702            clean_memo_probe_repeats: 0,
4703            clean_memo_sparse_samples: 0,
4704            fast_recognize_scratch: FastRecognizeTopScratch::default(),
4705            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
4706            empty_recovery_symbols: Rc::new(BTreeSet::new()),
4707            fast_first_set_prefilter: true,
4708            fast_recovery_enabled: true,
4709            fast_token_nodes_enabled: true,
4710            recognition_arena: RecognitionArena::default(),
4711            last_recognition_arena_root: NodeSeqId::EMPTY,
4712            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
4713        }
4714    }
4715
4716    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
4717        &mut self.input
4718    }
4719
4720    /// Fully resets parser-owned state and rewinds the current token stream.
4721    ///
4722    /// Parser configuration, semantic hooks, learned DFA tables, and
4723    /// grammar-owned member values are retained.
4724    pub fn reset(&mut self) {
4725        self.input.seek(0);
4726        self.tree.reset();
4727        self.data.set_state(-1);
4728        self.syntax_errors = 0;
4729        self.prediction_diagnostics.clear();
4730        self.reported_prediction_diagnostics.clear();
4731        self.generated_parser_diagnostics.clear();
4732        self.generated_sync_expected = None;
4733        self.rule_context_stack.clear();
4734        self.advance_rule_context_version();
4735        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
4736        self.pending_invoking_states.clear();
4737        self.precedence_stack.clear();
4738        self.precedence_stack.push(0);
4739        self.invoked_predicates.clear();
4740        self.unknown_predicate_hits.clear();
4741        self.unhandled_action_hits.clear();
4742        self.reset_per_parse_caches();
4743        self.fast_first_set_prefilter = true;
4744        self.fast_recovery_enabled = true;
4745        self.fast_token_nodes_enabled = self.build_parse_trees;
4746        self.reset_recognition_arena();
4747    }
4748
4749    /// Replaces the buffered token stream and fully resets this parser.
4750    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
4751        self.input = input;
4752        self.reset();
4753    }
4754
4755    /// Installs the policy for predicate coordinates that no translated table
4756    /// entry or user hook resolves.
4757    ///
4758    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
4759    /// but generated recursive-descent rules evaluate predicates directly
4760    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
4761    /// going through those options. Generated parser constructors call this so
4762    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
4763    /// the field at its `AssumeTrue` default and silently accepting an
4764    /// unimplemented hook predicate.
4765    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
4766        self.unknown_predicate_policy = policy;
4767    }
4768
4769    /// Reports any unknown predicate coordinate the generated-direct path
4770    /// recorded under [`UnknownSemanticPolicy::Error`], as an
4771    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
4772    /// after a rule completes so the fail-loud policy surfaces on the
4773    /// generated path the same way the interpreter entry surfaces it.
4774    #[must_use]
4775    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
4776        let error = self.unknown_semantic_error();
4777        self.unknown_predicate_hits.clear();
4778        self.unhandled_action_hits.clear();
4779        error
4780    }
4781
4782    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
4783    ///
4784    /// Generated parsers call this at the true top-level entry so a parser
4785    /// reused after a fail-loud (or recovered) parse starts clean, without
4786    /// clearing hits mid-parse where a generated parent still needs a child's
4787    /// recorded coordinate to survive to the top-level boundary.
4788    pub fn reset_unknown_semantic_hits(&mut self) {
4789        self.unknown_predicate_hits.clear();
4790        self.unhandled_action_hits.clear();
4791    }
4792
4793    /// Returns the token stream owned by this parser.
4794    #[must_use]
4795    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
4796        &self.input
4797    }
4798
4799    /// Returns the token stream for source replacement or in-place re-feeding.
4800    #[must_use]
4801    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
4802        &mut self.input
4803    }
4804
4805    /// Returns the canonical token store referenced by parse trees.
4806    #[must_use]
4807    pub const fn token_store(&self) -> &TokenStore {
4808        self.input.token_store()
4809    }
4810
4811    /// Returns the flat CST storage populated by completed rules.
4812    #[must_use]
4813    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
4814        &self.tree
4815    }
4816
4817    /// Resolves a compact parse-tree ID into a borrowing node view.
4818    #[must_use]
4819    pub fn node(&self, id: NodeId) -> Node<'_> {
4820        self.tree
4821            .node(self.input.token_store(), id)
4822            .expect("parser-produced node ID should remain valid")
4823    }
4824
4825    /// Consumes this parser and returns its token stream.
4826    #[must_use]
4827    pub fn into_token_stream(self) -> CommonTokenStream<S> {
4828        self.input
4829    }
4830
4831    /// Consumes this parser and returns its canonical token store.
4832    #[must_use]
4833    pub fn into_token_store(self) -> TokenStore {
4834        self.input.into_token_store()
4835    }
4836
4837    /// Consumes the parser and pairs its token store and flat CST with `root`.
4838    #[must_use]
4839    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
4840        ParsedFile::new(self.input.into_token_store(), self.tree, root)
4841    }
4842
4843    /// Returns the number of parser syntax errors recorded by committed parse
4844    /// paths so far.
4845    pub const fn number_of_syntax_errors(&self) -> usize {
4846        self.syntax_errors
4847    }
4848
4849    /// Computes reachability and retained-capacity counters for the most recent
4850    /// interpreted-rule recognition arena.
4851    ///
4852    /// The reachability scan is linear in the arena size and is deferred until
4853    /// this instrumentation method is called.
4854    #[must_use]
4855    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
4856        self.recognition_arena.stats(
4857            self.last_recognition_arena_root,
4858            self.last_recognition_arena_diagnostics,
4859        )
4860    }
4861
4862    /// Records a syntax error that generated parser code returns as fatal before
4863    /// it can recover into the current rule context.
4864    pub const fn record_generated_syntax_error(&mut self) {
4865        self.record_syntax_errors(1);
4866    }
4867
4868    const fn record_syntax_errors(&mut self, count: usize) {
4869        self.syntax_errors = self.syntax_errors.saturating_add(count);
4870    }
4871
4872    /// Emits diagnostics buffered by the token stream while generated parser
4873    /// code was fetching lexer tokens directly.
4874    pub fn report_token_source_errors(&mut self) {
4875        let errors = self.input.drain_source_errors();
4876        self.dispatch_token_source_errors(&errors);
4877    }
4878
4879    /// Captures generated-parser diagnostics and syntax-error count before a
4880    /// speculative generated rule path.
4881    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
4882        GeneratedDiagnosticsCheckpoint {
4883            diagnostics_len: self.generated_parser_diagnostics.len(),
4884            syntax_errors: self.syntax_errors,
4885            tree: self.tree.checkpoint(),
4886        }
4887    }
4888
4889    /// Restores generated-parser diagnostics after a speculative rule path failed.
4890    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
4891        self.generated_parser_diagnostics
4892            .truncate(marker.diagnostics_len);
4893        self.syntax_errors = marker.syntax_errors;
4894        self.generated_sync_expected = None;
4895        self.tree.rollback(marker.tree);
4896    }
4897
4898    /// Emits diagnostics recorded by committed generated parser recovery.
4899    pub fn report_generated_parser_diagnostics(&mut self) {
4900        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
4901        let token_errors = self.input.drain_source_errors();
4902        self.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
4903    }
4904
4905    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
4906        self.notify_error_listeners(
4907            diagnostic.line,
4908            diagnostic.column,
4909            &diagnostic.message,
4910            None,
4911        );
4912    }
4913
4914    fn dispatch_parser_diagnostics<'a>(
4915        &self,
4916        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
4917    ) {
4918        for diagnostic in diagnostics {
4919            self.dispatch_parser_diagnostic(diagnostic);
4920        }
4921    }
4922
4923    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
4924        if self.input.token_source().report_error(source_error) {
4925            return;
4926        }
4927        self.notify_error_listeners(
4928            source_error.line,
4929            source_error.column,
4930            &source_error.message,
4931            None,
4932        );
4933    }
4934
4935    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
4936        for error in errors {
4937            self.dispatch_token_source_error(error);
4938        }
4939    }
4940
4941    /// Dispatches generated parser and lexer diagnostics in the same
4942    /// source-position order as ANTLR's lazy token stream reports them.
4943    fn dispatch_generated_diagnostics(
4944        &self,
4945        parser_diagnostics: &[ParserDiagnostic],
4946        token_errors: &[TokenSourceError],
4947    ) {
4948        // Parser diagnostics keep their event order: Java's console and
4949        // DiagnosticErrorListener print reports as prediction produces them,
4950        // so reportAttemptingFullContext precedes reportContextSensitivity
4951        // even though the latter's position is earlier. Buffered token-source
4952        // errors interleave by source position and win ties.
4953        let mut token_iter = token_errors.iter().peekable();
4954        for diagnostic in parser_diagnostics {
4955            while let Some(error) = token_iter.peek() {
4956                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
4957                    self.dispatch_token_source_error(error);
4958                    token_iter.next();
4959                } else {
4960                    break;
4961                }
4962            }
4963            self.dispatch_parser_diagnostic(diagnostic);
4964        }
4965        for error in token_iter {
4966            self.dispatch_token_source_error(error);
4967        }
4968    }
4969
4970    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
4971    /// decision code.
4972    pub fn record_generated_ambiguity_diagnostic(
4973        &mut self,
4974        atn: &Atn,
4975        state_number: usize,
4976        start_index: usize,
4977        stop_index: usize,
4978        alts: &[usize],
4979    ) {
4980        if !self.report_diagnostic_errors || alts.len() < 2 {
4981            return;
4982        }
4983        let Some(decision) = atn
4984            .decision_to_state()
4985            .iter()
4986            .position(|candidate| candidate == state_number)
4987        else {
4988            return;
4989        };
4990        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
4991            return;
4992        };
4993        let rule_name = self
4994            .rule_names()
4995            .get(rule_index)
4996            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
4997        let input = display_input_text(&self.input.text(start_index, stop_index));
4998        let alts = alts
4999            .iter()
5000            .map(usize::to_string)
5001            .collect::<Vec<_>>()
5002            .join(", ");
5003        let key = (decision, start_index, format!("{alts}:{input}"));
5004        if !self.reported_prediction_diagnostics.insert(key) {
5005            return;
5006        }
5007        let start_diagnostic = diagnostic_for_token(
5008            self.token_at(start_index),
5009            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5010        );
5011        let stop_diagnostic = diagnostic_for_token(
5012            self.token_at(stop_index),
5013            format!(
5014                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5015            ),
5016        );
5017        self.generated_parser_diagnostics.push(start_diagnostic);
5018        self.generated_parser_diagnostics.push(stop_diagnostic);
5019    }
5020
5021    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5022    /// parser calls to the adaptive simulator.
5023    pub fn record_generated_prediction_diagnostic(
5024        &mut self,
5025        atn: &Atn,
5026        state_number: usize,
5027        prediction: &ParserAtnPrediction,
5028    ) {
5029        let Some(diagnostic) = &prediction.diagnostic else {
5030            return;
5031        };
5032        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5033            return;
5034        }
5035        let Some(decision) = atn
5036            .decision_to_state()
5037            .iter()
5038            .position(|candidate| candidate == state_number)
5039        else {
5040            return;
5041        };
5042        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5043            return;
5044        };
5045        let rule_name = self
5046            .rule_names()
5047            .get(rule_index)
5048            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5049        let attempt_input = display_input_text(
5050            &self
5051                .input
5052                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5053        );
5054        let result_input = display_input_text(
5055            &self
5056                .input
5057                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5058        );
5059        let alts = diagnostic
5060            .conflicting_alts
5061            .iter()
5062            .map(usize::to_string)
5063            .collect::<Vec<_>>()
5064            .join(", ");
5065        let key = (
5066            decision,
5067            diagnostic.start_index,
5068            format!(
5069                "{:?}:{alts}:{attempt_input}:{result_input}",
5070                diagnostic.kind
5071            ),
5072        );
5073        if !self.reported_prediction_diagnostics.insert(key) {
5074            return;
5075        }
5076        let attempt_diagnostic = diagnostic_for_token(
5077            self.token_at(diagnostic.sll_stop_index),
5078            format!(
5079                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5080            ),
5081        );
5082        self.generated_parser_diagnostics.push(attempt_diagnostic);
5083        let message = match diagnostic.kind {
5084            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5085                // Java's DiagnosticErrorListener is exactOnly by default:
5086                // non-exact ambiguities (default LL mode stopping at the
5087                // first resolvable conflict) report the attempt above but
5088                // suppress the ambiguity line itself.
5089                if !diagnostic.exact {
5090                    return;
5091                }
5092                format!(
5093                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5094                )
5095            }
5096            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5097                format!(
5098                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5099                )
5100            }
5101        };
5102        let result_diagnostic =
5103            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5104        self.generated_parser_diagnostics.push(result_diagnostic);
5105    }
5106
5107    pub fn la(&self, offset: isize) -> i32 {
5108        self.input.la_token(offset)
5109    }
5110
5111    pub fn consume(&mut self) {
5112        IntStream::consume(&mut self.input);
5113    }
5114
5115    /// Sets a generated integer member value used by target-template tests.
5116    pub fn set_int_member(&mut self, member: usize, value: i64) {
5117        self.int_members.insert(member, value);
5118    }
5119
5120    /// Reads a generated integer member value.
5121    pub fn int_member(&self, member: usize) -> Option<i64> {
5122        self.int_members.get(&member).copied()
5123    }
5124
5125    /// Captures generated integer members before speculative generated parser
5126    /// execution.
5127    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
5128        self.int_members.clone()
5129    }
5130
5131    /// Restores generated integer members after generated parser fallback.
5132    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
5133        self.int_members = members;
5134    }
5135
5136    /// Adds `delta` to a generated integer member and returns the new value.
5137    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5138        let value = self.int_members.entry(member).or_default();
5139        *value += delta;
5140        *value
5141    }
5142
5143    fn token_type_for_id(&self, id: TokenId) -> i32 {
5144        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5145    }
5146
5147    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5148        if self.build_parse_trees {
5149            self.tree.terminal(id)
5150        } else {
5151            NodeId::placeholder()
5152        }
5153    }
5154
5155    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5156        if self.build_parse_trees {
5157            self.tree.error(id)
5158        } else {
5159            NodeId::placeholder()
5160        }
5161    }
5162
5163    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5164        context.set_start_id(id);
5165    }
5166
5167    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5168        context.set_stop_id(id);
5169    }
5170
5171    fn insert_synthetic_token(
5172        &mut self,
5173        token_type: i32,
5174        text: String,
5175        line: usize,
5176        column: usize,
5177    ) -> Result<TokenId, AntlrError> {
5178        self.input
5179            .insert(
5180                TokenSpec::explicit(token_type, text)
5181                    .with_span(usize::MAX, usize::MAX)
5182                    .with_byte_span(0, 0)
5183                    .with_position(line, column),
5184            )
5185            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5186    }
5187
5188    /// Matches and consumes the current token when it has the expected token
5189    /// type.
5190    ///
5191    /// On success the consumed token is wrapped as a terminal parse-tree node.
5192    /// On mismatch the error carries vocabulary display names so diagnostics are
5193    /// stable across literal and symbolic token naming.
5194    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5195        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5196            line: 0,
5197            column: 0,
5198            message: "missing current token".to_owned(),
5199        })?;
5200        let current_type = self.token_type_for_id(current);
5201        if current_type == token_type {
5202            self.consume();
5203            Ok(self.terminal_tree(current))
5204        } else {
5205            Err(AntlrError::MismatchedInput {
5206                expected: self.vocabulary().display_name(token_type),
5207                found: self.vocabulary().display_name(current_type),
5208            })
5209        }
5210    }
5211
5212    /// Matches a token from generated recursive-descent code, including ANTLR's
5213    /// single-token insertion recovery when the active rule context can legally
5214    /// continue at the current input symbol.
5215    pub fn match_token_recovering(
5216        &mut self,
5217        token_type: i32,
5218        follow_state: usize,
5219        atn: &Atn,
5220    ) -> Result<GeneratedMatch, AntlrError> {
5221        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5222            line: 0,
5223            column: 0,
5224            message: "missing current token".to_owned(),
5225        })?;
5226        let current_type = self.token_type_for_id(current);
5227        if current_type == token_type {
5228            self.generated_sync_expected = None;
5229            let consumed_eof = current_type == TOKEN_EOF;
5230            self.consume();
5231            return Ok(GeneratedMatch {
5232                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5233                consumed_eof,
5234            });
5235        }
5236        let mut expected_symbols = BTreeSet::new();
5237        expected_symbols.insert(token_type);
5238        self.recover_generated_match(
5239            current,
5240            GeneratedExpectedSymbols::Tree(&expected_symbols),
5241            follow_state,
5242            atn,
5243            |symbol| symbol == token_type,
5244        )
5245    }
5246
5247    pub fn match_set_recovering(
5248        &mut self,
5249        intervals: &[(i32, i32)],
5250        follow_state: usize,
5251        atn: &Atn,
5252    ) -> Result<GeneratedMatch, AntlrError> {
5253        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5254            line: 0,
5255            column: 0,
5256            message: "missing current token".to_owned(),
5257        })?;
5258        let current_type = self.token_type_for_id(current);
5259        if interval_set_contains(intervals, current_type) {
5260            self.generated_sync_expected = None;
5261            let consumed_eof = current_type == TOKEN_EOF;
5262            self.consume();
5263            return Ok(GeneratedMatch {
5264                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5265                consumed_eof,
5266            });
5267        }
5268        let expected_symbols = interval_symbols(intervals);
5269        self.recover_generated_match(
5270            current,
5271            GeneratedExpectedSymbols::Tree(&expected_symbols),
5272            follow_state,
5273            atn,
5274            |symbol| interval_set_contains(intervals, symbol),
5275        )
5276    }
5277
5278    pub fn match_token_set_recovering(
5279        &mut self,
5280        set: ParserIntervalSet<'_>,
5281        follow_state: usize,
5282        atn: &Atn,
5283    ) -> Result<GeneratedMatch, AntlrError> {
5284        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5285            line: 0,
5286            column: 0,
5287            message: "missing current token".to_owned(),
5288        })?;
5289        let current_type = self.token_type_for_id(current);
5290        if set.contains(current_type) {
5291            self.generated_sync_expected = None;
5292            let consumed_eof = current_type == TOKEN_EOF;
5293            self.consume();
5294            return Ok(GeneratedMatch {
5295                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5296                consumed_eof,
5297            });
5298        }
5299        self.recover_generated_match(
5300            current,
5301            GeneratedExpectedSymbols::TokenSet(set),
5302            follow_state,
5303            atn,
5304            |symbol| set.contains(symbol),
5305        )
5306    }
5307
5308    pub fn match_not_set_recovering(
5309        &mut self,
5310        intervals: &[(i32, i32)],
5311        min_vocabulary: i32,
5312        max_vocabulary: i32,
5313        follow_state: usize,
5314        atn: &Atn,
5315    ) -> Result<GeneratedMatch, AntlrError> {
5316        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5317            line: 0,
5318            column: 0,
5319            message: "missing current token".to_owned(),
5320        })?;
5321        let current_type = self.token_type_for_id(current);
5322        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5323            && !interval_set_contains(intervals, current_type)
5324        {
5325            self.generated_sync_expected = None;
5326            let consumed_eof = current_type == TOKEN_EOF;
5327            self.consume();
5328            return Ok(GeneratedMatch {
5329                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5330                consumed_eof,
5331            });
5332        }
5333        let expected_symbols =
5334            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5335        self.recover_generated_match(
5336            current,
5337            GeneratedExpectedSymbols::Tree(&expected_symbols),
5338            follow_state,
5339            atn,
5340            |symbol| {
5341                (min_vocabulary..=max_vocabulary).contains(&symbol)
5342                    && !interval_set_contains(intervals, symbol)
5343            },
5344        )
5345    }
5346
5347    pub fn match_not_token_set_recovering(
5348        &mut self,
5349        set: ParserIntervalSet<'_>,
5350        min_vocabulary: i32,
5351        max_vocabulary: i32,
5352        follow_state: usize,
5353        atn: &Atn,
5354    ) -> Result<GeneratedMatch, AntlrError> {
5355        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5356            line: 0,
5357            column: 0,
5358            message: "missing current token".to_owned(),
5359        })?;
5360        let current_type = self.token_type_for_id(current);
5361        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
5362        {
5363            self.generated_sync_expected = None;
5364            let consumed_eof = current_type == TOKEN_EOF;
5365            self.consume();
5366            return Ok(GeneratedMatch {
5367                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5368                consumed_eof,
5369            });
5370        }
5371        self.recover_generated_match(
5372            current,
5373            GeneratedExpectedSymbols::TokenSetComplement {
5374                set,
5375                min_vocabulary,
5376                max_vocabulary,
5377            },
5378            follow_state,
5379            atn,
5380            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
5381        )
5382    }
5383
5384    fn recover_generated_match(
5385        &mut self,
5386        current: TokenId,
5387        expected_symbols: GeneratedExpectedSymbols<'_>,
5388        follow_state: usize,
5389        atn: &Atn,
5390        matches: impl Fn(i32) -> bool,
5391    ) -> Result<GeneratedMatch, AntlrError> {
5392        let expected_display = expected_symbols.display(self.vocabulary());
5393        let (current_type, current_line, current_column, current_display) = {
5394            let token = self
5395                .input
5396                .token_view(current)
5397                .expect("current token ID should be valid");
5398            (
5399                token.token_type(),
5400                token.line(),
5401                token.column(),
5402                token_input_display(&token),
5403            )
5404        };
5405        if self.bail_on_error {
5406            return Err(AntlrError::ParserError {
5407                line: current_line,
5408                column: current_column,
5409                message: format!("mismatched input {current_display} expecting {expected_display}"),
5410            });
5411        }
5412        if current_type != TOKEN_EOF
5413            && let Some(next) = self.input.lt_id(2)
5414            && matches(self.token_type_for_id(next))
5415        {
5416            let message =
5417                format!("extraneous input {current_display} expecting {expected_display}");
5418            self.push_generated_parser_diagnostic(ParserDiagnostic {
5419                line: current_line,
5420                column: current_column,
5421                message,
5422            });
5423            self.record_syntax_errors(1);
5424            self.generated_sync_expected = None;
5425            // Single-token deletion: skip `current`, then accept `next`. The
5426            // accepted token can be EOF only if it is a real EOF terminal.
5427            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5428            self.consume();
5429            self.consume();
5430            return Ok(GeneratedMatch {
5431                children: GeneratedMatchChildren::Many(vec![
5432                    self.error_tree(current),
5433                    self.terminal_tree(next),
5434                ]),
5435                consumed_eof,
5436            });
5437        }
5438        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5439        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5440        // *after* the current element can consume the current symbol. At EOF that
5441        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5442        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5443        // when EOF merely leaks in from the empty enclosing context (as in
5444        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5445        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5446        // so consult the follow state's OWN expected set for the explicit case.
5447        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5448            && self
5449                .cached_state_expected_symbols(atn, follow_state)
5450                .contains(&TOKEN_EOF);
5451        if follow_symbols.contains(&current_type)
5452            && (current_type != TOKEN_EOF
5453                || self.rule_context_stack.len() > 1
5454                || expected_symbols.is_empty()
5455                || follow_explicitly_expects_eof)
5456        {
5457            let message = format!("missing {expected_display} at {current_display}");
5458            self.push_generated_parser_diagnostic(ParserDiagnostic {
5459                line: current_line,
5460                column: current_column,
5461                message,
5462            });
5463            self.record_syntax_errors(1);
5464            self.generated_sync_expected = None;
5465            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
5466            let missing_display = expected_symbol_display(token_type, self.vocabulary());
5467            let token = self.insert_synthetic_token(
5468                token_type,
5469                format!("<missing {missing_display}>"),
5470                current_line,
5471                current_column,
5472            )?;
5473            // Single-token insertion synthesizes a missing token and consumes
5474            // nothing, so no EOF terminal is consumed even when the lookahead is
5475            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5476            // from recording EOF as the rule stop on this recovery path.
5477            return Ok(GeneratedMatch {
5478                children: GeneratedMatchChildren::One(self.error_tree(token)),
5479                consumed_eof: false,
5480            });
5481        }
5482        let mismatch_expected_display = self
5483            .generated_sync_expected
5484            .take()
5485            .map_or(expected_display, |symbols| {
5486                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
5487            });
5488        Err(AntlrError::ParserError {
5489            line: current_line,
5490            column: current_column,
5491            message: format!(
5492                "mismatched input {current_display} expecting {mismatch_expected_display}"
5493            ),
5494        })
5495    }
5496
5497    fn generated_recovery_follow_symbols(
5498        &mut self,
5499        atn: &Atn,
5500        follow_state: usize,
5501    ) -> BTreeSet<i32> {
5502        let mut follow = self
5503            .cached_state_expected_symbols(atn, follow_state)
5504            .as_ref()
5505            .clone();
5506        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5507            follow.extend(self.context_expected_symbols(atn));
5508        }
5509        follow
5510    }
5511
5512    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5513        self.match_token(TOKEN_EOF)
5514    }
5515
5516    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5517        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5518    }
5519
5520    pub fn match_not_set(
5521        &mut self,
5522        intervals: &[(i32, i32)],
5523        min_vocabulary: i32,
5524        max_vocabulary: i32,
5525    ) -> Result<ParseTree, AntlrError> {
5526        self.match_interval_condition(intervals, |symbol| {
5527            (min_vocabulary..=max_vocabulary).contains(&symbol)
5528                && !interval_set_contains(intervals, symbol)
5529        })
5530    }
5531
5532    fn match_interval_condition(
5533        &mut self,
5534        intervals: &[(i32, i32)],
5535        matches: impl FnOnce(i32) -> bool,
5536    ) -> Result<ParseTree, AntlrError> {
5537        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5538            line: 0,
5539            column: 0,
5540            message: "missing current token".to_owned(),
5541        })?;
5542        let current_type = self.token_type_for_id(current);
5543        if matches(current_type) {
5544            self.consume();
5545            Ok(self.terminal_tree(current))
5546        } else {
5547            Err(AntlrError::MismatchedInput {
5548                expected: self.interval_display(intervals),
5549                found: self.vocabulary().display_name(current_type),
5550            })
5551        }
5552    }
5553
5554    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5555        let values = intervals
5556            .iter()
5557            .map(|(start, stop)| {
5558                if start == stop {
5559                    self.vocabulary().display_name(*start)
5560                } else {
5561                    format!(
5562                        "{}..{}",
5563                        self.vocabulary().display_name(*start),
5564                        self.vocabulary().display_name(*stop)
5565                    )
5566                }
5567            })
5568            .collect::<Vec<_>>()
5569            .join(", ");
5570        format!("{{{values}}}")
5571    }
5572
5573    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5574        if self.build_parse_trees {
5575            self.tree.finish_rule(context)
5576        } else {
5577            NodeId::placeholder()
5578        }
5579    }
5580
5581    /// Enters a generated parser rule and returns the context object the
5582    /// generated method should populate.
5583    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
5584        self.set_state(state);
5585        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
5586        self.rule_context_stack.push(RuleContextFrame {
5587            rule_index,
5588            invoking_state,
5589        });
5590        self.advance_rule_context_version();
5591        let start_index = self.current_visible_index();
5592        let mut context = ParserRuleContext::new(rule_index, invoking_state);
5593        if let Some(token) = self.token_id_at(start_index) {
5594            self.set_context_start(&mut context, token);
5595        }
5596        context
5597    }
5598
5599    /// Records the ATN source state for the next generated rule invocation.
5600    ///
5601    /// ANTLR's full-context prediction reconstructs caller follow states from
5602    /// each active rule context's invoking state. Generated Rust rule methods are
5603    /// plain functions, so the caller supplies that ATN state just before making a
5604    /// rule call; `enter_rule` consumes it when the callee starts.
5605    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
5606        let marker = self.pending_invoking_states.len();
5607        self.pending_invoking_states.push(invoking_state);
5608        marker
5609    }
5610
5611    /// Discards an invoking-state marker if the callee did not consume it.
5612    pub fn discard_invoking_state(&mut self, marker: usize) {
5613        self.pending_invoking_states.truncate(marker);
5614    }
5615
5616    /// Exits the current generated parser rule.
5617    pub fn exit_rule(&mut self) {
5618        self.rule_context_stack.pop();
5619        self.advance_rule_context_version();
5620    }
5621
5622    /// Returns caller follow states for interning in a parser ATN simulator's
5623    /// prediction store. States are yielded outermost to innermost.
5624    pub fn prediction_context_return_states<'a>(
5625        &'a self,
5626        atn: &'a Atn,
5627    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
5628        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
5629            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
5630                return None;
5631            };
5632            let Some(Transition::Rule { follow_state, .. }) = atn
5633                .state(state_number)
5634                .and_then(|state| state.transitions().first())
5635                .map(ParserTransition::data)
5636            else {
5637                return None;
5638            };
5639            Some(follow_state)
5640        })
5641    }
5642
5643    /// Returns a generation that changes whenever the active rule stack changes.
5644    ///
5645    /// A parser ATN simulator uses this to reuse an interned outer prediction
5646    /// context while generated predictions remain in the same rule context.
5647    pub const fn rule_context_version(&self) -> usize {
5648        self.rule_context_version
5649    }
5650
5651    const fn advance_rule_context_version(&mut self) {
5652        self.rule_context_version = self.rule_context_version.wrapping_add(1);
5653    }
5654
5655    /// Adds a generated parser child only when parse-tree construction is
5656    /// enabled. The match is recorded on the context either way (via `add_child`,
5657    /// or `note_matched_child` when trees are off) so generated recovery can tell
5658    /// whether the rule has matched anything yet without depending on `children`.
5659    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
5660        if self.build_parse_trees {
5661            self.tree.add_child(context, child);
5662        } else {
5663            context.note_matched_child();
5664        }
5665    }
5666
5667    fn release_tree_scratch_if_idle(&mut self) {
5668        if self.rule_context_stack.is_empty() {
5669            self.tree.release_scratch();
5670        }
5671    }
5672
5673    /// Finishes a generated parser rule and returns its parse-tree node.
5674    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
5675        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5676        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5677            self.set_context_stop(&mut context, token);
5678        }
5679        let node = self.rule_node(context);
5680        self.exit_rule();
5681        self.release_tree_scratch_if_idle();
5682        node
5683    }
5684
5685    /// Recovers a generated rule catch block after a committed mismatch.
5686    ///
5687    /// ANTLR's generated parsers catch recognition errors inside each rule,
5688    /// report the original error, then consume unexpected tokens until the
5689    /// caller's recovery set can resume. Tokens consumed during recovery become
5690    /// error nodes in the current rule context.
5691    pub fn recover_generated_rule(
5692        &mut self,
5693        context: &mut ParserRuleContext,
5694        atn: &Atn,
5695        error: AntlrError,
5696    ) {
5697        let diagnostic = self.generated_rule_error_diagnostic(error);
5698        self.push_generated_parser_diagnostic(diagnostic);
5699        self.generated_sync_expected = None;
5700        let recovery_symbols = self.context_expected_symbols(atn);
5701        loop {
5702            let symbol = self.la(1);
5703            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
5704                break;
5705            }
5706            let Some(token) = self.input.lt_id(1) else {
5707                break;
5708            };
5709            self.consume();
5710            let child = self.error_tree(token);
5711            self.add_parse_child(context, child);
5712        }
5713        self.record_syntax_errors(1);
5714    }
5715
5716    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
5717        if self
5718            .generated_parser_diagnostics
5719            .iter()
5720            .any(|existing| existing == &diagnostic)
5721        {
5722            return;
5723        }
5724        self.generated_parser_diagnostics.push(diagnostic);
5725    }
5726
5727    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
5728        match error {
5729            AntlrError::ParserError {
5730                line,
5731                column,
5732                message,
5733            } => ParserDiagnostic {
5734                line,
5735                column,
5736                message,
5737            },
5738            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
5739                self.input.lt(1),
5740                format!("mismatched input {found} expecting {expected}"),
5741            ),
5742            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
5743                self.input.lt(1),
5744                format!("no viable alternative at input {input}"),
5745            ),
5746            AntlrError::LexerError {
5747                line,
5748                column,
5749                message,
5750            } => ParserDiagnostic {
5751                line,
5752                column,
5753                message,
5754            },
5755            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
5756        }
5757    }
5758
5759    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
5760    pub fn finish_recursion_rule(
5761        &mut self,
5762        mut context: ParserRuleContext,
5763        consumed_eof: bool,
5764    ) -> ParseTree {
5765        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
5766        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
5767            self.set_context_stop(&mut context, token);
5768        }
5769        let node = self.rule_node(context);
5770        self.unroll_recursion_context();
5771        self.release_tree_scratch_if_idle();
5772        node
5773    }
5774
5775    /// Enters a generated left-recursive rule at `precedence`.
5776    pub fn enter_recursion_rule(
5777        &mut self,
5778        state: isize,
5779        rule_index: usize,
5780        precedence: i32,
5781    ) -> ParserRuleContext {
5782        self.precedence_stack.push(precedence);
5783        self.enter_rule(state, rule_index)
5784    }
5785
5786    /// Replaces the current context while expanding a left-recursive rule.
5787    pub fn push_new_recursion_context(
5788        &mut self,
5789        state: isize,
5790        rule_index: usize,
5791    ) -> ParserRuleContext {
5792        self.set_state(state);
5793        ParserRuleContext::new(rule_index, state)
5794    }
5795
5796    /// Wraps the previous left-recursive context before parsing the next
5797    /// recursive operator alternative.
5798    pub fn push_new_recursion_context_with_previous(
5799        &mut self,
5800        state: isize,
5801        rule_index: usize,
5802        current: &mut ParserRuleContext,
5803    ) {
5804        self.set_state(state);
5805        if let Some(stop) = self
5806            .rule_stop_token_index(self.input.index(), false)
5807            .and_then(|index| self.token_id_at(index))
5808        {
5809            self.set_context_stop(current, stop);
5810        }
5811        let invoking_state = current.invoking_state();
5812        let start = current.start_id();
5813        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
5814        if start.is_some() {
5815            replacement.set_start_from_context(current);
5816        }
5817        let previous = std::mem::replace(current, replacement);
5818        if self.build_parse_trees {
5819            let previous = self.rule_node(previous);
5820            self.tree.add_child(current, previous);
5821        }
5822    }
5823
5824    /// Leaves a generated left-recursive rule.
5825    pub fn unroll_recursion_context(&mut self) {
5826        if self.precedence_stack.len() > 1 {
5827            self.precedence_stack.pop();
5828        }
5829        self.exit_rule();
5830    }
5831
5832    /// Predicts a generated left-recursive loop from one-token lookahead.
5833    ///
5834    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
5835    /// `None` means caller overlap, a dangerous multi-token prefix, or an
5836    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
5837    /// prediction (which includes the exit alt and precedence filtering).
5838    ///
5839    /// Single-token operators and multi-token prefixes that do not shadow a
5840    /// lower-precedence single-token operator keep the one-token enter fast path.
5841    ///
5842    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
5843    /// operator must not force enter; the adaptive decision may need to select
5844    /// the loop exit instead.
5845    pub fn left_recursive_loop_enter_prediction(
5846        &mut self,
5847        atn: &Atn,
5848        state_number: usize,
5849        precedence: i32,
5850    ) -> Option<bool> {
5851        let symbol = self.la(1);
5852        if symbol == TOKEN_EOF {
5853            return Some(false);
5854        }
5855        let operator_lookahead =
5856            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
5857        let can_single = operator_lookahead.single_token.contains(symbol);
5858        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
5859        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
5860        if !can_single && !can_multi && !can_predicate {
5861            return Some(false);
5862        }
5863        if can_predicate && !can_single {
5864            return None;
5865        }
5866        // Multi-token-only at this precedence, but the same symbol is a
5867        // single-token operator at precedence 0: defer so exit can win when the
5868        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
5869        if !can_single && can_multi && precedence > 0 {
5870            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
5871            if baseline.single_token.contains(symbol) {
5872                return None;
5873            }
5874        }
5875        let atn_key = SharedAtnCacheKey::for_atn(atn);
5876        let cached_overlap = self
5877            .left_recursive_caller_overlap_cache
5878            .iter()
5879            .flatten()
5880            .find(|entry| {
5881                entry.atn_key == atn_key
5882                    && entry.state_number == state_number
5883                    && entry.symbol == symbol
5884                    && entry.context_version == self.rule_context_version
5885            })
5886            .map(|entry| entry.overlaps);
5887        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
5888            let overlaps = caller_context_can_match_symbol_before_state(
5889                atn,
5890                self.prediction_context_return_states(atn),
5891                state_number,
5892                symbol,
5893            );
5894            if let Some(slot) = self
5895                .left_recursive_caller_overlap_cache
5896                .iter_mut()
5897                .find(|slot| slot.is_none())
5898            {
5899                *slot = Some(LeftRecursiveCallerOverlap {
5900                    atn_key,
5901                    state_number,
5902                    symbol,
5903                    context_version: self.rule_context_version,
5904                    overlaps,
5905                });
5906            }
5907            overlaps
5908        });
5909        if caller_overlaps {
5910            return None;
5911        }
5912        Some(true)
5913    }
5914
5915    fn cached_left_recursive_operator_lookahead(
5916        atn: &Atn,
5917        state_number: usize,
5918        precedence: i32,
5919    ) -> Rc<LeftRecursiveOperatorLookahead> {
5920        with_shared_atn_caches(atn, |cache| {
5921            let key = (state_number, precedence);
5922            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
5923                return Rc::clone(cached);
5924            }
5925            let lookahead = Rc::new(left_recursive_operator_lookahead(
5926                atn,
5927                state_number,
5928                precedence,
5929            ));
5930            cache
5931                .left_recursive_operator_lookahead
5932                .insert(key, Rc::clone(&lookahead));
5933            lookahead
5934        })
5935    }
5936
5937    /// Checks whether a generated left-recursive loop can unambiguously enter
5938    /// its operator alternative from one-token lookahead.
5939    pub fn left_recursive_loop_enter_matches(
5940        &mut self,
5941        atn: &Atn,
5942        state_number: usize,
5943        precedence: i32,
5944    ) -> bool {
5945        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
5946    }
5947
5948    /// Implements generated `precpred(_ctx, k)` checks.
5949    pub fn precpred(&self, precedence: i32) -> bool {
5950        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
5951    }
5952
5953    /// Evaluates a generated parser semantic predicate at the current input
5954    /// position.
5955    pub fn parser_semantic_predicate_matches(
5956        &mut self,
5957        predicates: &[(usize, usize, ParserPredicate)],
5958        rule_index: usize,
5959        pred_index: usize,
5960    ) -> bool {
5961        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
5962    }
5963
5964    /// Evaluates a generated parser semantic predicate with the current integer
5965    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
5966    pub fn parser_semantic_predicate_matches_with_local(
5967        &mut self,
5968        predicates: &[(usize, usize, ParserPredicate)],
5969        rule_index: usize,
5970        pred_index: usize,
5971        local_int_arg: i32,
5972    ) -> bool {
5973        self.parser_semantic_predicate_matches_inner(
5974            predicates,
5975            rule_index,
5976            pred_index,
5977            Some((rule_index, i64::from(local_int_arg))),
5978        )
5979    }
5980
5981    fn parser_semantic_predicate_matches_inner(
5982        &mut self,
5983        predicates: &[(usize, usize, ParserPredicate)],
5984        rule_index: usize,
5985        pred_index: usize,
5986        local_int_arg: Option<(usize, i64)>,
5987    ) -> bool {
5988        let index = self.input.index();
5989        let member_values = self.int_members.clone();
5990        self.parser_predicate_matches(PredicateEval {
5991            index,
5992            rule_index,
5993            pred_index,
5994            predicates,
5995            semantics: None,
5996            context: None,
5997            local_int_arg,
5998            member_values: &member_values,
5999        })
6000    }
6001
6002    /// Evaluates a generated parser semantic predicate with access to the
6003    /// current generated rule context.
6004    pub fn parser_semantic_predicate_matches_with_context_and_local(
6005        &mut self,
6006        predicates: &[(usize, usize, ParserPredicate)],
6007        rule_index: usize,
6008        pred_index: usize,
6009        context: &ParserRuleContext,
6010        local_int_arg: i32,
6011    ) -> bool {
6012        let index = self.input.index();
6013        let member_values = self.int_members.clone();
6014        self.parser_predicate_matches(PredicateEval {
6015            index,
6016            rule_index,
6017            pred_index,
6018            predicates,
6019            semantics: None,
6020            context: Some(context),
6021            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6022            member_values: &member_values,
6023        })
6024    }
6025
6026    /// Evaluates a generated `SemIR` parser predicate with access to the current
6027    /// generated rule context.
6028    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
6029        &mut self,
6030        semantics: &ParserSemantics,
6031        rule_index: usize,
6032        pred_index: usize,
6033        context: &ParserRuleContext,
6034        local_int_arg: i32,
6035    ) -> bool {
6036        let index = self.input.index();
6037        let member_values = self.int_members.clone();
6038        self.parser_predicate_matches(PredicateEval {
6039            index,
6040            rule_index,
6041            pred_index,
6042            predicates: &[],
6043            semantics: Some(semantics),
6044            context: Some(context),
6045            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6046            member_values: &member_values,
6047        })
6048    }
6049
6050    /// Returns a generated fail-option message for a parser semantic
6051    /// predicate coordinate.
6052    pub fn parser_semantic_predicate_failure_message(
6053        &self,
6054        rule_index: usize,
6055        pred_index: usize,
6056        predicates: &[(usize, usize, ParserPredicate)],
6057    ) -> Option<&'static str> {
6058        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
6059    }
6060
6061    /// Matches any non-EOF token.
6062    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
6063        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6064            line: 0,
6065            column: 0,
6066            message: "missing current token".to_owned(),
6067        })?;
6068        if self.token_type_for_id(current) == TOKEN_EOF {
6069            return Err(AntlrError::MismatchedInput {
6070                expected: "wildcard".to_owned(),
6071                found: self.vocabulary().display_name(TOKEN_EOF),
6072            });
6073        }
6074        self.consume();
6075        Ok(self.terminal_tree(current))
6076    }
6077
6078    /// Generated parser synchronization hook. The current interpreter owns
6079    /// recovery; direct generated methods can call this as a no-op until the
6080    /// generated recovery strategy is expanded.
6081    #[allow(clippy::unnecessary_wraps)]
6082    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
6083        self.set_state(state);
6084        Ok(())
6085    }
6086
6087    /// Synchronizes a generated parser decision against the ATN lookahead set.
6088    ///
6089    /// ANTLR generated parsers call the error strategy before optional and loop
6090    /// decisions. When the current token cannot start any alternative, follow a
6091    /// nullable exit, or be deleted before a later synchronization token, the
6092    /// generated Rust method reports that decision-level mismatch instead of
6093    /// descending into a child rule that cannot start at the current token.
6094    pub fn sync_decision(
6095        &mut self,
6096        atn: &Atn,
6097        state_number: usize,
6098        current_context_empty: bool,
6099        loop_back: bool,
6100    ) -> Result<Vec<ParseTree>, AntlrError> {
6101        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
6102        self.generated_sync_expected = None;
6103        let Some(state) = atn.state(state_number) else {
6104            return Ok(Vec::new());
6105        };
6106        let Some(rule_index) = state.rule_index() else {
6107            return Ok(Vec::new());
6108        };
6109        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
6110            return Ok(Vec::new());
6111        };
6112        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6113        let symbol = self.la(1);
6114        let mut has_expected_symbols = false;
6115        let mut nullable = false;
6116        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
6117        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
6118        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
6119        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
6120        // and worth deleting; an implicit-follow EOF means the loop should simply
6121        // exit and leave the token for the (absent) caller — matching ANTLR, which
6122        // exits the loop via prediction rather than consuming up to a synthetic EOF.
6123        let mut explicit_eof_expected = false;
6124        for transition in &entry.transitions {
6125            if transition.symbols.contains(symbol) {
6126                return Ok(Vec::new());
6127            }
6128            has_expected_symbols |= !transition.symbols.is_empty();
6129            nullable |= transition.nullable;
6130            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
6131        }
6132        // Happy path: a nullable decision exits when the symbol is in the
6133        // rule-stack follow set. Answer the membership question with an
6134        // early-exit walk; the full union below is only needed for the
6135        // mismatch/deletion diagnostics.
6136        if nullable && self.context_expected_contains(atn, symbol) {
6137            return Ok(Vec::new());
6138        }
6139        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
6140        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
6141            return Ok(Vec::new());
6142        }
6143        let mut expected = TokenBitSet::default();
6144        for transition in &entry.transitions {
6145            expected.extend_from(&transition.symbols);
6146        }
6147        if let Some(context_expected) = context_expected {
6148            expected.extend_from(&context_expected);
6149        }
6150        let can_delete_in_place =
6151            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
6152        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
6153        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
6154        // least one iteration) does `consumeUntil` the follow set — multi-token
6155        // deletion, one error per skipped token across iterations; a loop ENTRY
6156        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
6157        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
6158        // unexpected token only when LA(2) is expected, otherwise report a mismatch
6159        // and leave recovery to the rule.
6160        //
6161        // The generated loop always presents the loop-ENTRY state to this method on
6162        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
6163        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
6164        // an iteration has been taken, and true on a `+` loop's first sync since its
6165        // mandatory first element is iteration 1). Treating a loop entry as a
6166        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
6167        // `c`s, which ANTLR rejects with `mismatched input`).
6168        let loop_sync = loop_back;
6169        if symbol != TOKEN_EOF && can_delete_in_place {
6170            let mut cursor = self.input.index();
6171            let mut skipped = Vec::new();
6172            loop {
6173                let current = self.token_type_at(cursor);
6174                if current == TOKEN_EOF {
6175                    break;
6176                }
6177                skipped.push(cursor);
6178                let next = self.consume_index(cursor, current);
6179                if next == cursor {
6180                    break;
6181                }
6182                let next_symbol = self.token_type_at(next);
6183                // Stop (and delete the skipped tokens as error nodes) when the next
6184                // token is a real expected continuation. EOF counts only when it is
6185                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
6186                // genuinely extraneous and the generated EOF match consumes the real
6187                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
6188                // rule-follow) does NOT count — the loop must exit and leave the
6189                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
6190                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
6191                    explicit_eof_expected
6192                } else {
6193                    expected.contains(next_symbol)
6194                };
6195                if next_is_expected_stop {
6196                    let current_token = self.input.lt(1);
6197                    let expected_symbols = expected.to_btree_set();
6198                    let message = format!(
6199                        "extraneous input {} expecting {}",
6200                        current_token
6201                            .as_ref()
6202                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6203                        self.expected_symbols_display(&expected_symbols)
6204                    );
6205                    self.push_generated_parser_diagnostic(diagnostic_for_token(
6206                        current_token,
6207                        message,
6208                    ));
6209                    self.record_syntax_errors(1);
6210                    let mut children = Vec::with_capacity(skipped.len());
6211                    for index in skipped {
6212                        if let Some(token) = self.token_id_at(index) {
6213                            self.consume();
6214                            children.push(self.error_tree(token));
6215                        }
6216                    }
6217                    return Ok(children);
6218                }
6219                // A non-loop block entry deletes at most one token (single-token
6220                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6221                // is reported at the first token instead of skipping ahead.
6222                if !loop_sync {
6223                    break;
6224                }
6225                cursor = next;
6226            }
6227        }
6228        if nullable {
6229            self.generated_sync_expected = Some(expected);
6230            return Ok(Vec::new());
6231        }
6232        let current = self.input.lt(1);
6233        let expected_symbols = expected.to_btree_set();
6234        Err(AntlrError::ParserError {
6235            line: current.as_ref().map(Token::line).unwrap_or_default(),
6236            column: current.as_ref().map(Token::column).unwrap_or_default(),
6237            message: format!(
6238                "mismatched input {} expecting {}",
6239                current
6240                    .as_ref()
6241                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6242                self.expected_symbols_display(&expected_symbols)
6243            ),
6244        })
6245    }
6246
6247    /// Returns a generated-parser prediction when one token of lookahead
6248    /// uniquely selects an alternative for `state_number`.
6249    ///
6250    /// This mirrors the interpreter's LL(1) commit point and lets generated
6251    /// recursive-descent methods avoid invoking the adaptive simulator for
6252    /// simple optional/block/loop decisions.
6253    pub fn ll1_decision_prediction(
6254        &mut self,
6255        atn: &Atn,
6256        state_number: usize,
6257    ) -> Option<ParserAtnPrediction> {
6258        let state = atn.state(state_number)?;
6259        if state.precedence_rule_decision() {
6260            return None;
6261        }
6262        let rule_stop = state
6263            .rule_index()
6264            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6265        let symbol = self.la(1);
6266        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6267        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6268            alt: alt + 1,
6269            requires_full_context: false,
6270            has_semantic_context: false,
6271            diagnostic: None,
6272        })
6273    }
6274
6275    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6276        let mut expected = BTreeSet::new();
6277        for index in (1..self.rule_context_stack.len()).rev() {
6278            let invoking_state = self.rule_context_stack[index].invoking_state;
6279            let Ok(state_number) = usize::try_from(invoking_state) else {
6280                continue;
6281            };
6282            let Some(Transition::Rule { follow_state, .. }) = atn
6283                .state(state_number)
6284                .and_then(|state| state.transitions().first())
6285                .map(ParserTransition::data)
6286            else {
6287                continue;
6288            };
6289            let return_state = follow_state;
6290            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6291            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
6292                return expected;
6293            }
6294        }
6295        expected.insert(TOKEN_EOF);
6296        expected
6297    }
6298
6299    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6300        let mut expected = TokenBitSet::default();
6301        for index in (1..self.rule_context_stack.len()).rev() {
6302            let invoking_state = self.rule_context_stack[index].invoking_state;
6303            let Ok(state_number) = usize::try_from(invoking_state) else {
6304                continue;
6305            };
6306            let Some(Transition::Rule { follow_state, .. }) = atn
6307                .state(state_number)
6308                .and_then(|state| state.transitions().first())
6309                .map(ParserTransition::data)
6310            else {
6311                continue;
6312            };
6313            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
6314            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6315                return expected;
6316            }
6317        }
6318        expected.insert(TOKEN_EOF);
6319        expected
6320    }
6321
6322    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
6323    /// without materializing the union.
6324    ///
6325    /// This walks the rule-invocation stack directly, innermost frame first —
6326    /// the same frames, in the same order, with the same rule-stop gating as
6327    /// the same outer-context return-state chain used by adaptive prediction.
6328    /// The nullable
6329    /// exit in `sync_decision` asks only this membership question, and on
6330    /// valid input the innermost frame answers it, so the early exit replaces
6331    /// an O(stack-depth) set union per loop/optional exit with one probe.
6332    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
6333        for index in (1..self.rule_context_stack.len()).rev() {
6334            let invoking_state = self.rule_context_stack[index].invoking_state;
6335            let Ok(state_number) = usize::try_from(invoking_state) else {
6336                continue;
6337            };
6338            let Some(Transition::Rule { follow_state, .. }) = atn
6339                .state(state_number)
6340                .and_then(|state| state.transitions().first())
6341                .map(ParserTransition::data)
6342            else {
6343                continue;
6344            };
6345            if self
6346                .cached_state_expected_token_set(atn, follow_state)
6347                .contains(symbol)
6348            {
6349                return true;
6350            }
6351            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6352                return false;
6353            }
6354        }
6355        symbol == TOKEN_EOF
6356    }
6357
6358    /// Builds a generated no-viable-alternative parser error.
6359    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
6360        let error_index = self.input.index();
6361        self.no_viable_alternative_error_at(start_index, error_index)
6362    }
6363
6364    /// Builds a generated no-viable-alternative parser error at the simulator's
6365    /// failing lookahead index. `adaptive_predict` restores the input cursor
6366    /// before returning, so generated parsers have to pass the recorded index
6367    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
6368    pub fn no_viable_alternative_error_at(
6369        &self,
6370        start_index: usize,
6371        error_index: usize,
6372    ) -> AntlrError {
6373        let diagnostic = self.no_viable_alternative(start_index, error_index);
6374        AntlrError::ParserError {
6375            line: diagnostic.line,
6376            column: diagnostic.column,
6377            message: diagnostic.message,
6378        }
6379    }
6380
6381    /// Builds a generated failed-predicate parser error.
6382    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
6383        let current = self.input.lt(1);
6384        AntlrError::ParserError {
6385            line: current.as_ref().map(Token::line).unwrap_or_default(),
6386            column: current.as_ref().map(Token::column).unwrap_or_default(),
6387            message: format!("rule failed predicate: {}", message.into()),
6388        }
6389    }
6390
6391    /// Builds a generated parser error for a semantic predicate with ANTLR's
6392    /// `<fail='...'>` option.
6393    pub fn failed_predicate_option_error(
6394        &self,
6395        rule_index: usize,
6396        message: impl Into<String>,
6397    ) -> AntlrError {
6398        let current = self.input.lt(1);
6399        let rule_name = self
6400            .rule_names()
6401            .get(rule_index)
6402            .map_or_else(|| rule_index.to_string(), Clone::clone);
6403        AntlrError::ParserError {
6404            line: current.as_ref().map(Token::line).unwrap_or_default(),
6405            column: current.as_ref().map(Token::column).unwrap_or_default(),
6406            message: format!("rule {rule_name} {}", message.into()),
6407        }
6408    }
6409
6410    /// Builds a generated parser-action event at the current input position.
6411    pub fn parser_action_at_current(
6412        &mut self,
6413        source_state: usize,
6414        rule_index: usize,
6415        start_index: usize,
6416        consumed_eof: bool,
6417    ) -> ParserAction {
6418        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6419        ParserAction::new(source_state, rule_index, start_index, stop_index)
6420    }
6421
6422    /// Offers a committed parser action event to the user semantic hook.
6423    ///
6424    /// Generated parsers call this for action source states that were present
6425    /// in the ATN but not translated into a built-in Rust action template.
6426    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
6427        let rule_index = action.rule_index();
6428        let rule_name = self.rule_names().get(rule_index).cloned();
6429        let context = None;
6430        let input = &mut self.input;
6431        let semantic_hooks = &mut self.semantic_hooks;
6432        let member_values = &self.int_members;
6433        let mut ctx = ParserSemCtx {
6434            input,
6435            tree_storage: &self.tree,
6436            rule_index,
6437            coordinate_index: usize::MAX,
6438            rule_name,
6439            context,
6440            tree: Some(tree),
6441            local_int_arg: None,
6442            member_values,
6443            action: Some(action),
6444        };
6445        let handled = semantic_hooks.action(&mut ctx, action);
6446        // This action reached the hook because it had no translated arm. If no
6447        // hook handled it either (`SemanticHooks::action` returns `false`), the
6448        // committed action is silently dropped — record it so the parse entry
6449        // can fail loud under the fail-loud boundary, mirroring unknown
6450        // predicates. `assume-*` policies opt out of the fail-loud recording.
6451        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
6452            let coordinate = (rule_index, action.source_state());
6453            if !self.unhandled_action_hits.contains(&coordinate) {
6454                self.unhandled_action_hits.push(coordinate);
6455            }
6456        }
6457        handled
6458    }
6459
6460    /// Attempts to execute a whole generated rule by committing simulator
6461    /// decisions directly. Unsupported constructs or decisions that need
6462    /// full-context / predicate evaluation restore the input cursor and fall
6463    /// back to [`Self::parse_atn_rule`].
6464    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
6465        &mut self,
6466        atn: &'atn Atn,
6467        simulator: &mut ParserAtnSimulator<'atn>,
6468        rule_index: usize,
6469    ) -> Result<ParseTree, AntlrError> {
6470        let start_index = self.current_visible_index();
6471        self.clear_prediction_diagnostics();
6472        self.reset_per_parse_caches();
6473        self.reset_recognition_arena();
6474        let tree_checkpoint = self.tree.checkpoint();
6475        let mut decision_by_state = vec![None; atn.states().len()];
6476        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
6477            if let Some(slot) = decision_by_state.get_mut(state_number) {
6478                *slot = Some(decision);
6479            }
6480        }
6481
6482        let result = DirectAdaptiveParser {
6483            parser: self,
6484            atn,
6485            simulator,
6486            decision_by_state,
6487            steps: 0,
6488        }
6489        .parse_rule(rule_index, -1, 0);
6490
6491        match result {
6492            Ok(tree) => {
6493                self.report_token_source_errors();
6494                self.release_tree_scratch_if_idle();
6495                Ok(tree)
6496            }
6497            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
6498                let _ = reason;
6499                self.tree.rollback(tree_checkpoint);
6500                self.input.seek(start_index);
6501                self.parse_atn_rule(atn, rule_index)
6502            }
6503        }
6504    }
6505
6506    /// Parses a generated rule by interpreting the parser ATN from the rule's
6507    /// start state to its stop state.
6508    ///
6509    /// The recognizer backtracks across alternatives and loop exits using token
6510    /// stream indices instead of committing to input consumption immediately.
6511    /// Once a viable ATN path is found, the parser commits the accepted token
6512    /// interval and returns a rule node whose children mirror every grammar
6513    /// rule invocation reached on that path, matching ANTLR's parse-tree
6514    /// shape.
6515    pub fn parse_atn_rule(
6516        &mut self,
6517        atn: &Atn,
6518        rule_index: usize,
6519    ) -> Result<ParseTree, AntlrError> {
6520        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
6521    }
6522
6523    /// Parses a generated rule by interpreting the parser ATN with an initial
6524    /// left-recursive precedence threshold.
6525    pub fn parse_atn_rule_with_precedence(
6526        &mut self,
6527        atn: &Atn,
6528        rule_index: usize,
6529        precedence: i32,
6530    ) -> Result<ParseTree, AntlrError> {
6531        self.parse_atn_rule_with_precedence_inner(atn, rule_index, precedence, None)
6532    }
6533
6534    fn parse_atn_rule_with_precedence_inner(
6535        &mut self,
6536        atn: &Atn,
6537        rule_index: usize,
6538        precedence: i32,
6539        predicate_context: Option<FastPredicateContext<'_>>,
6540    ) -> Result<ParseTree, AntlrError> {
6541        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
6542            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
6543        })?;
6544        let stop_state = atn
6545            .rule_to_stop_state()
6546            .get(rule_index)
6547            .filter(|state| *state != usize::MAX)
6548            .ok_or_else(|| {
6549                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
6550            })?;
6551
6552        let start_index = self.current_visible_index();
6553        self.clear_prediction_diagnostics();
6554        self.reset_per_parse_caches();
6555        self.reset_recognition_arena();
6556        let caller_follow_state = self.pending_invoking_follow_state(atn);
6557        self.fast_recovery_enabled = false;
6558        self.fast_token_nodes_enabled = false;
6559        let top_request = FastRecognizeTopRequest {
6560            start_state,
6561            stop_state,
6562            start_index,
6563            precedence,
6564            caller_follow_state,
6565        };
6566        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
6567        self.fast_token_nodes_enabled = self.build_parse_trees;
6568        let needs_tree_retry = matches!(
6569            &first_pass,
6570            Ok((outcome, _))
6571                if self.build_parse_trees
6572                    && self
6573                        .recognition_arena
6574                        .sequence_has_left_recursive_boundary(outcome.nodes)
6575        );
6576        let needs_retry = match &first_pass {
6577            // The FIRST-set prefilter trims speculative rule calls that can't
6578            // match the current lookahead — useful for perf on grammars with
6579            // many epsilon-reachable rules, but the trim also bypasses
6580            // single-token insertion / deletion recovery that ANTLR's
6581            // reference parser runs at the child rule's first consuming
6582            // transition. Retry without the prefilter whenever the first pass
6583            // either produced no outcome at all or produced a recovered
6584            // outcome (diagnostics non-empty), since the second pass might
6585            // surface a child-level recovery with cleaner diagnostics or
6586            // closer parity to ANTLR's tree shape. Left-recursive tree
6587            // boundaries also need the token-node pass; otherwise the fold has
6588            // no concrete left operand to wrap into ANTLR's recursive context.
6589            Err(_) => true,
6590            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
6591        };
6592        let (outcome, _expected) = if needs_retry {
6593            self.fast_first_set_prefilter = false;
6594            self.fast_recovery_enabled = false;
6595            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6596            let clean_selected = if needs_tree_retry {
6597                match clean_retry {
6598                    ok @ Ok(_) => ok,
6599                    Err(_) => first_pass,
6600                }
6601            } else {
6602                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
6603            };
6604            let selected = if clean_selected.is_err()
6605                || matches!(&clean_selected, Ok((outcome, _)) if !outcome.diagnostics.is_empty())
6606            {
6607                self.fast_recovery_enabled = true;
6608                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
6609                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
6610            } else {
6611                clean_selected
6612            };
6613            self.fast_first_set_prefilter = true;
6614            self.fast_recovery_enabled = true;
6615            selected.map_err(|expected| {
6616                if predicate_context.is_some()
6617                    && let Some(error) = self.unknown_semantic_error()
6618                {
6619                    self.report_token_source_errors();
6620                    return error;
6621                }
6622                let error = self.recognition_error(rule_index, start_index, &expected);
6623                self.record_syntax_errors(1);
6624                self.report_token_source_errors();
6625                error
6626            })?
6627        } else {
6628            first_pass.expect("first_pass is Ok in the no-retry branch")
6629        };
6630        if predicate_context.is_some()
6631            && let Some(error) = self.unknown_semantic_error()
6632        {
6633            self.report_token_source_errors();
6634            return Err(error);
6635        }
6636        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
6637        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
6638        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
6639        self.report_token_source_errors();
6640        let mut context = ParserRuleContext::with_child_capacity(
6641            rule_index,
6642            self.state(),
6643            if self.build_parse_trees {
6644                self.recognition_arena.sequence_len(outcome.nodes)
6645            } else {
6646                0
6647            },
6648        );
6649        if let Some(token) = self.token_id_at(start_index) {
6650            self.set_context_start(&mut context, token);
6651        }
6652        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
6653        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
6654            self.set_context_stop(&mut context, token);
6655        }
6656        let live_root = if self.build_parse_trees {
6657            self.recognition_arena
6658                .fold_left_recursive_boundaries(outcome.nodes)
6659        } else {
6660            outcome.nodes
6661        };
6662        if self.build_parse_trees {
6663            if self
6664                .recognition_arena
6665                .sequence_has_explicit_token(live_root)
6666            {
6667                let mut cursor = live_root;
6668                while let Some(link) = self.recognition_arena.link(cursor) {
6669                    let child = self.arena_recognized_node_tree(link.head, false)?;
6670                    self.tree.add_child(&mut context, child);
6671                    cursor = link.tail;
6672                }
6673            } else {
6674                self.add_arena_implicit_token_children(
6675                    &mut context,
6676                    start_index,
6677                    stop_index,
6678                    live_root,
6679                )?;
6680            }
6681        }
6682        self.finish_recognition_arena(live_root, outcome.diagnostics);
6683        self.input.seek(outcome.index);
6684
6685        let tree = self.rule_node(context);
6686        self.release_tree_scratch_if_idle();
6687        Ok(tree)
6688    }
6689
6690    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
6691        let invoking_state = self.pending_invoking_states.last().copied()?;
6692        let state_number = usize::try_from(invoking_state).ok()?;
6693        match atn.state(state_number)?.transitions().first()?.data() {
6694            Transition::Rule { follow_state, .. } => Some(follow_state),
6695            _ => None,
6696        }
6697    }
6698
6699    #[cfg(test)]
6700    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
6701        caller_follow_token_info_for_stream(&mut self.input, index)
6702    }
6703
6704    /// Runs the fast recognizer once from the rule's start state and returns
6705    /// the best outcome or the per-attempt expected-token accumulator. The
6706    /// caller flips `fast_first_set_prefilter` between calls when a retry is
6707    /// needed, so the FIRST-set cache is left intact across both passes.
6708    fn fast_recognize_top(
6709        &mut self,
6710        atn: &Atn,
6711        request: FastRecognizeTopRequest,
6712        predicate_context: Option<FastPredicateContext<'_>>,
6713    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
6714        let FastRecognizeTopRequest {
6715            start_state,
6716            stop_state,
6717            start_index,
6718            precedence,
6719            caller_follow_state,
6720        } = request;
6721        // `input.size()` is intentionally only the currently buffered token
6722        // count here. Do not restore an up-front fill just to size this map:
6723        // a small floor avoids tiny-input churn, and larger inputs reserve from
6724        // the buffered token count without forcing startup tokenization. The
6725        // 8x multiplier matches the empirical
6726        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
6727        // Kotlin ~12× memo entries per token), so the table avoids one
6728        // rehash on the typical hot path.
6729        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
6730        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
6731        recognize_scratch.prepare(memo_capacity);
6732        let mut expected = ExpectedTokens::default();
6733        let empty_recovery = self.empty_recovery_symbols();
6734        let outcomes = self.recognize_state_fast(
6735            atn,
6736            FastRecognizeRequest {
6737                state_number: start_state,
6738                stop_state,
6739                index: start_index,
6740                rule_start_index: start_index,
6741                decision_start_index: None,
6742                precedence,
6743                depth: 0,
6744                recovery_symbols: empty_recovery,
6745                recovery_state: None,
6746            },
6747            FastRecognizeScratch {
6748                predicate_context,
6749                visiting: &mut recognize_scratch.visiting,
6750                memo: &mut recognize_scratch.memo,
6751                expected: &mut expected,
6752            },
6753        );
6754        recognize_scratch.release_oversized_memo();
6755        self.fast_recognize_scratch = recognize_scratch;
6756        #[cfg(feature = "perf-counters")]
6757        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
6758            perf_counters::dump();
6759            perf_counters::reset();
6760        }
6761        let caller_follow =
6762            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
6763        let selected = {
6764            let arena = &self.recognition_arena;
6765            let input = &mut self.input;
6766            select_best_fast_outcome(
6767                outcomes.into_iter(),
6768                self.prediction_mode,
6769                caller_follow.as_deref(),
6770                |index| caller_follow_token_info_for_stream(input, index),
6771                arena,
6772            )
6773        };
6774        match selected {
6775            Some(mut outcome) => {
6776                if self.build_parse_trees {
6777                    self.materialize_fast_outcome_nodes(&mut outcome);
6778                }
6779                Ok((outcome, expected))
6780            }
6781            None => Err(expected),
6782        }
6783    }
6784
6785    /// Converts one speculative arena record into the flat public CST.
6786    fn arena_recognized_node_tree(
6787        &mut self,
6788        node_id: RecognizedNodeId,
6789        track_alt_numbers: bool,
6790    ) -> Result<ParseTree, AntlrError> {
6791        let node = self.recognition_arena.node(node_id);
6792        match node {
6793            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
6794            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
6795            ArenaRecognizedNode::MissingToken { extra } => {
6796                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
6797                    RecognitionExtra::MissingToken {
6798                        token_type,
6799                        at_index,
6800                        text,
6801                    } => (*token_type, *at_index as usize, text.clone()),
6802                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
6803                        unreachable!("missing-token node must reference missing-token extra")
6804                    }
6805                };
6806                let (line, column) = self
6807                    .token_at(at_index)
6808                    .map_or((0, 0), |token| (token.line(), token.column()));
6809                let token = self.insert_synthetic_token(token_type, text, line, column)?;
6810                Ok(self.error_tree(token))
6811            }
6812            ArenaRecognizedNode::Rule {
6813                rule_index,
6814                invoking_state,
6815                alt_number,
6816                start_index,
6817                stop_index,
6818                return_values,
6819                children,
6820            } => {
6821                let mut context = ParserRuleContext::with_child_capacity(
6822                    rule_index as usize,
6823                    invoking_state as isize,
6824                    self.recognition_arena.sequence_len(children),
6825                );
6826                if track_alt_numbers {
6827                    context.set_alt_number(alt_number as usize);
6828                }
6829                if let Some(extra) = return_values {
6830                    let RecognitionExtra::ReturnValues(values) =
6831                        self.recognition_arena.extra(extra)
6832                    else {
6833                        unreachable!("rule node must reference return-values extra");
6834                    };
6835                    for (name, value) in values {
6836                        context.set_int_return(name.clone(), *value);
6837                    }
6838                }
6839                if let Some(token) = self.token_id_at(start_index as usize) {
6840                    self.set_context_start(&mut context, token);
6841                }
6842                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6843                    self.set_context_stop(&mut context, token);
6844                }
6845                let mut cursor = self
6846                    .recognition_arena
6847                    .fold_left_recursive_boundaries(children);
6848                while let Some(link) = self.recognition_arena.link(cursor) {
6849                    let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
6850                    self.tree.add_child(&mut context, child);
6851                    cursor = link.tail;
6852                }
6853                Ok(self.rule_node(context))
6854            }
6855            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index } => {
6856                Err(AntlrError::Unsupported(format!(
6857                    "unfolded left-recursive boundary for rule {rule_index}"
6858                )))
6859            }
6860        }
6861    }
6862
6863    fn arena_recognized_node_tree_with_implicit_tokens(
6864        &mut self,
6865        node_id: RecognizedNodeId,
6866    ) -> Result<ParseTree, AntlrError> {
6867        let node = self.recognition_arena.node(node_id);
6868        match node {
6869            ArenaRecognizedNode::Rule {
6870                rule_index,
6871                invoking_state,
6872                start_index,
6873                stop_index,
6874                children,
6875                ..
6876            } => {
6877                let mut context = ParserRuleContext::with_child_capacity(
6878                    rule_index as usize,
6879                    invoking_state as isize,
6880                    self.recognition_arena.sequence_len(children),
6881                );
6882                if let Some(token) = self.token_id_at(start_index as usize) {
6883                    self.set_context_start(&mut context, token);
6884                }
6885                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
6886                    self.set_context_stop(&mut context, token);
6887                }
6888                let children = self
6889                    .recognition_arena
6890                    .fold_left_recursive_boundaries(children);
6891                self.add_arena_implicit_token_children(
6892                    &mut context,
6893                    start_index as usize,
6894                    stop_index.map(|index| index as usize),
6895                    children,
6896                )?;
6897                Ok(self.rule_node(context))
6898            }
6899            _ => self.arena_recognized_node_tree(node_id, false),
6900        }
6901    }
6902
6903    fn add_arena_implicit_token_children(
6904        &mut self,
6905        context: &mut ParserRuleContext,
6906        start_index: usize,
6907        stop_index: Option<usize>,
6908        mut children: NodeSeqId,
6909    ) -> Result<(), AntlrError> {
6910        let mut cursor = Some(start_index);
6911        while let Some(link) = self.recognition_arena.link(children) {
6912            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
6913                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
6914                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6915                self.tree.add_child(context, child);
6916                if let Some(child_stop) = child_stop {
6917                    cursor = self.next_visible_after_token(child_stop);
6918                }
6919            } else {
6920                let child = self.arena_recognized_node_tree_with_implicit_tokens(link.head)?;
6921                self.tree.add_child(context, child);
6922            }
6923            children = link.tail;
6924        }
6925        if let Some(stop) = stop_index {
6926            self.add_visible_terminals_through(context, cursor, stop)?;
6927        }
6928        Ok(())
6929    }
6930
6931    fn add_visible_terminals_before(
6932        &mut self,
6933        context: &mut ParserRuleContext,
6934        cursor: &mut Option<usize>,
6935        before: usize,
6936    ) -> Result<(), AntlrError> {
6937        let Some(stop) = before.checked_sub(1) else {
6938            return Ok(());
6939        };
6940        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
6941        *cursor = next;
6942        Ok(())
6943    }
6944
6945    fn add_visible_terminals_through(
6946        &mut self,
6947        context: &mut ParserRuleContext,
6948        mut cursor: Option<usize>,
6949        stop: usize,
6950    ) -> Result<Option<usize>, AntlrError> {
6951        while let Some(index) = cursor {
6952            if index > stop {
6953                return Ok(Some(index));
6954            }
6955            let token = self
6956                .input
6957                .get_id(index)
6958                .ok_or_else(|| AntlrError::ParserError {
6959                    line: 0,
6960                    column: 0,
6961                    message: format!("missing token at index {index}"),
6962                })?;
6963            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
6964            let child = self.terminal_tree(token);
6965            self.tree.add_child(context, child);
6966            if is_eof {
6967                return Ok(None);
6968            }
6969            cursor = self.next_visible_after_token(index);
6970        }
6971        Ok(None)
6972    }
6973
6974    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
6975        let next = self.input.next_visible_after(index);
6976        (next != index).then_some(next)
6977    }
6978
6979    /// Parses a generated rule and returns semantic actions reached on the
6980    /// selected ATN path.
6981    ///
6982    /// This slower path preserves action ordering and token intervals for
6983    /// generated code that replays target-specific action templates after the
6984    /// recognizer has chosen one viable parse path.
6985    pub fn parse_atn_rule_with_actions(
6986        &mut self,
6987        atn: &Atn,
6988        rule_index: usize,
6989    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
6990        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
6991    }
6992
6993    /// Parses a generated rule and emits ATN actions plus selected rule-init
6994    /// actions reached on the chosen path.
6995    ///
6996    /// Generated parsers use this when a grammar contains rule-level `@init`
6997    /// templates that must run for nested rule invocations. The runtime keeps
6998    /// the action list path-sensitive, so init templates are replayed only for
6999    /// rules that were actually entered by the selected parse.
7000    pub fn parse_atn_rule_with_action_inits(
7001        &mut self,
7002        atn: &Atn,
7003        rule_index: usize,
7004        init_action_rules: &[usize],
7005    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7006        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7007    }
7008
7009    /// Parses a generated rule with optional semantic-action replay features.
7010    ///
7011    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7012    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7013    /// unchanged for grammars that do not request that target template.
7014    pub fn parse_atn_rule_with_action_options(
7015        &mut self,
7016        atn: &Atn,
7017        rule_index: usize,
7018        init_action_rules: &[usize],
7019        track_alt_numbers: bool,
7020    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7021        self.parse_atn_rule_with_runtime_options(
7022            atn,
7023            rule_index,
7024            ParserRuntimeOptions {
7025                init_action_rules,
7026                track_alt_numbers,
7027                ..ParserRuntimeOptions::default()
7028            },
7029        )
7030    }
7031
7032    /// Parses a generated rule with action replay and parser predicate support.
7033    ///
7034    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7035    /// target-template predicate semantics emitted by the generator. Missing
7036    /// entries are treated as true so unsupported predicate-free grammars keep
7037    /// the previous unconditional transition behavior.
7038    pub fn parse_atn_rule_with_runtime_options(
7039        &mut self,
7040        atn: &Atn,
7041        rule_index: usize,
7042        options: ParserRuntimeOptions<'_>,
7043    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7044        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7045    }
7046
7047    /// Parses a generated rule with action replay, parser predicate support,
7048    /// and an initial left-recursive precedence threshold.
7049    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7050        &mut self,
7051        atn: &Atn,
7052        rule_index: usize,
7053        precedence: i32,
7054        options: ParserRuntimeOptions<'_>,
7055    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7056        let ParserRuntimeOptions {
7057            init_action_rules,
7058            track_alt_numbers,
7059            predicates,
7060            semantics,
7061            rule_args,
7062            member_actions,
7063            return_actions,
7064            unknown_predicate_policy,
7065        } = options;
7066        if init_action_rules.is_empty()
7067            && !track_alt_numbers
7068            && predicates.is_empty()
7069            && semantics.is_none()
7070            && rule_args.is_empty()
7071            && member_actions.is_empty()
7072            && return_actions.is_empty()
7073            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7074            && !atn_has_observable_action_transitions(atn)
7075            && (!self.semantic_hooks.observes_parser_predicates()
7076                || !atn_has_predicate_transitions(atn))
7077        {
7078            return self
7079                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7080                .map(|tree| (tree, Vec::new()));
7081        }
7082        if can_use_fast_predicate_recognizer(atn, &options) {
7083            self.unknown_predicate_policy = unknown_predicate_policy;
7084            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7085            let member_values = self.int_members.clone();
7086            let result = self
7087                .parse_atn_rule_with_precedence_inner(
7088                    atn,
7089                    rule_index,
7090                    precedence,
7091                    Some(FastPredicateContext {
7092                        predicates,
7093                        semantics,
7094                        member_values: &member_values,
7095                    }),
7096                )
7097                .map(|tree| (tree, Vec::new()));
7098            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7099                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7100            }
7101            return result;
7102        }
7103        self.unknown_predicate_policy = unknown_predicate_policy;
7104        // A generated parent may have already recorded unknown-predicate
7105        // coordinates before descending into this (interpreted) child. Clearing
7106        // unconditionally would drop them before the parent's public entry
7107        // surfaces them, so stash and restore around this call: recognition sees
7108        // only the hits it records itself (so the fail-loud check below reflects
7109        // this rule), and the parent's prior hits are merged back afterward.
7110        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7111        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7112            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7113        })?;
7114        let stop_state = atn
7115            .rule_to_stop_state()
7116            .get(rule_index)
7117            .filter(|state| *state != usize::MAX)
7118            .ok_or_else(|| {
7119                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7120            })?;
7121
7122        let start_index = self.current_visible_index();
7123        self.clear_prediction_diagnostics();
7124        self.reset_per_parse_caches();
7125        self.reset_recognition_arena();
7126        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7127        let invoking_state = self.pending_invoking_states.pop();
7128        let local_int_arg = invoking_state
7129            .and_then(|state| usize::try_from(state).ok())
7130            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7131        let mut visiting = BTreeSet::new();
7132        let mut memo = BTreeMap::new();
7133        let mut expected = ExpectedTokens::default();
7134        let member_values = self.int_members.clone();
7135        let return_values = BTreeMap::new();
7136        let outcomes = self.recognize_state(
7137            atn,
7138            RecognizeRequest {
7139                state_number: start_state,
7140                stop_state,
7141                index: start_index,
7142                rule_start_index: start_index,
7143                decision_start_index: None,
7144                init_action_rules: &init_action_rules,
7145                predicates,
7146                semantics,
7147                rule_args,
7148                member_actions,
7149                return_actions,
7150                local_int_arg,
7151                member_values,
7152                return_values,
7153                rule_alt_number: 0,
7154                track_alt_numbers,
7155                consumed_eof: false,
7156                precedence,
7157                depth: 0,
7158                recovery_symbols: BTreeSet::new(),
7159                recovery_state: None,
7160            },
7161            &mut visiting,
7162            &mut memo,
7163            &mut expected,
7164        );
7165        if let Some(error) = self.unknown_semantic_error() {
7166            self.report_token_source_errors();
7167            // Keep the recorded coordinates: when this interpreted rule is a
7168            // child of a generated parent, the parent's catch block recovers an
7169            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7170            // coordinate must survive on the parser for the top-level entry's
7171            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7172            // is handled by clearing at the top-level generated entry instead.
7173            return Err(error);
7174        }
7175        // Recognition recorded no unresolved coordinate of its own; merge the
7176        // parent's prior hits back so its public entry can still surface them.
7177        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7178        let Some(outcome) = select_best_outcome(
7179            outcomes.into_iter(),
7180            self.prediction_mode,
7181            &self.recognition_arena,
7182        ) else {
7183            let error = self.recognition_error(rule_index, start_index, &expected);
7184            self.record_syntax_errors(1);
7185            self.report_token_source_errors();
7186            return Err(error);
7187        };
7188
7189        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7190        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7191        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7192        self.report_token_source_errors();
7193        let mut actions = outcome.actions;
7194        if init_action_rules.contains(&rule_index) {
7195            actions.insert(
7196                0,
7197                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7198            );
7199        }
7200        let mut context =
7201            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7202        if track_alt_numbers {
7203            context.set_alt_number(outcome.alt_number);
7204        }
7205        for (name, value) in outcome.return_values {
7206            context.set_int_return(name, value);
7207        }
7208        if let Some(token) = self.token_id_at(start_index) {
7209            self.set_context_start(&mut context, token);
7210        }
7211        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7212            self.set_context_stop(&mut context, token);
7213        }
7214        let live_root = if self.build_parse_trees {
7215            self.recognition_arena
7216                .fold_left_recursive_boundaries(outcome.nodes)
7217        } else {
7218            outcome.nodes
7219        };
7220        if self.build_parse_trees {
7221            let mut nodes = live_root;
7222            while let Some(link) = self.recognition_arena.link(nodes) {
7223                let child = self.arena_recognized_node_tree(link.head, track_alt_numbers)?;
7224                self.tree.add_child(&mut context, child);
7225                nodes = link.tail;
7226            }
7227        }
7228        self.finish_recognition_arena(live_root, outcome.diagnostics);
7229        self.input.seek(outcome.index);
7230
7231        let tree = self.rule_node(context);
7232        self.release_tree_scratch_if_idle();
7233        Ok((tree, actions))
7234    }
7235
7236    /// Temporary parser entry used by generated parser methods while the parser
7237    /// ATN simulator is being implemented.
7238    ///
7239    /// This keeps generated parser crates buildable and gives us a stable method
7240    /// surface for every grammar rule. It intentionally accepts all remaining
7241    /// tokens into one rule context; it is not the final parser semantics.
7242    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
7243        let mut context = ParserRuleContext::new(rule_index, self.state());
7244        while self.la(1) != TOKEN_EOF {
7245            let token_type = self.la(1);
7246            let child = self.match_token(token_type)?;
7247            if self.build_parse_trees {
7248                self.tree.add_child(&mut context, child);
7249            }
7250        }
7251        if self.build_parse_trees {
7252            let child = self.match_eof()?;
7253            self.tree.add_child(&mut context, child);
7254        }
7255        let tree = self.rule_node(context);
7256        self.release_tree_scratch_if_idle();
7257        Ok(tree)
7258    }
7259
7260    /// Builds the parser error reported when no ATN path can reach the active
7261    /// rule stop state.
7262    fn recognition_error(
7263        &mut self,
7264        rule_index: usize,
7265        start_index: usize,
7266        expected: &ExpectedTokens,
7267    ) -> AntlrError {
7268        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
7269        self.input.seek(index);
7270        let current = self.input.lt(1);
7271        let line = current.as_ref().map(Token::line).unwrap_or_default();
7272        let column = current.as_ref().map(Token::column).unwrap_or_default();
7273        AntlrError::ParserError {
7274            line,
7275            column,
7276            message,
7277        }
7278    }
7279
7280    /// Builds the token index and ANTLR-compatible message for a failed rule.
7281    fn expected_error_message(
7282        &mut self,
7283        rule_index: usize,
7284        start_index: usize,
7285        expected: &ExpectedTokens,
7286    ) -> (usize, String) {
7287        let index = expected
7288            .index
7289            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
7290            .unwrap_or_else(|| self.input.index());
7291        self.input.seek(index);
7292        let current = self.input.lt(1);
7293        let message = if expected
7294            .no_viable
7295            .as_ref()
7296            .is_some_and(|no_viable| no_viable.error_index == index)
7297        {
7298            let start = expected
7299                .no_viable
7300                .as_ref()
7301                .map_or(start_index, |no_viable| no_viable.start_index);
7302            let text = display_input_text(&self.input.text(start, index));
7303            format!("no viable alternative at input '{text}'")
7304        } else if expected.symbols.is_empty() {
7305            if expected.index.is_some() {
7306                let found = current
7307                    .as_ref()
7308                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7309                if current
7310                    .as_ref()
7311                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7312                {
7313                    format!(
7314                        "missing {} at {found}",
7315                        self.expected_symbols_display(&expected.symbols)
7316                    )
7317                } else {
7318                    format!("mismatched input {found}")
7319                }
7320            } else {
7321                format!("no viable alternative while parsing rule {rule_index}")
7322            }
7323        } else {
7324            format!(
7325                "mismatched input {} expecting {}",
7326                current
7327                    .as_ref()
7328                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7329                self.expected_symbols_display(&expected.symbols)
7330            )
7331        };
7332        (index, message)
7333    }
7334
7335    /// Converts a failed child rule into a recovered outcome so the parent can
7336    /// continue after reporting the child diagnostic.
7337    fn child_rule_failure_recovery(
7338        &mut self,
7339        rule_index: usize,
7340        start_index: usize,
7341        sync_symbols: &BTreeSet<i32>,
7342        member_values: BTreeMap<usize, i64>,
7343        expected: &ExpectedTokens,
7344    ) -> Option<RecognizeOutcome> {
7345        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7346        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7347        let mut next_index = error_index;
7348        loop {
7349            let symbol = self.token_type_at(next_index);
7350            if sync_symbols.contains(&symbol) {
7351                if next_index == error_index {
7352                    return None;
7353                }
7354                break;
7355            }
7356            if symbol == TOKEN_EOF {
7357                break;
7358            }
7359            let after = self.consume_index(next_index, symbol);
7360            if after == next_index {
7361                break;
7362            }
7363            next_index = after;
7364        }
7365        let mut nodes = NodeSeqId::EMPTY;
7366        let error = self.arena_token_node(error_index, true);
7367        self.arena_prepend(&mut nodes, error);
7368        let diagnostics = self
7369            .recognition_arena
7370            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7371        Some(RecognizeOutcome {
7372            index: next_index,
7373            consumed_eof: false,
7374            alt_number: 0,
7375            member_values,
7376            return_values: BTreeMap::new(),
7377            diagnostics,
7378            decisions: Vec::new(),
7379            actions: Vec::new(),
7380            nodes,
7381        })
7382    }
7383
7384    /// Adapts the optional recovery result to the normal outcome list used by
7385    /// rule-call transitions.
7386    fn child_rule_failure_recovery_outcomes(
7387        &mut self,
7388        request: ChildRuleFailureRecovery<'_>,
7389    ) -> Vec<RecognizeOutcome> {
7390        let sync_symbols =
7391            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
7392        self.child_rule_failure_recovery(
7393            request.rule_index,
7394            request.start_index,
7395            &sync_symbols,
7396            request.member_values,
7397            request.expected,
7398        )
7399        .into_iter()
7400        .collect()
7401    }
7402
7403    /// Formats expected token types using ANTLR's single-token or set syntax.
7404    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
7405        expected_symbols_display(symbols, self.vocabulary())
7406    }
7407
7408    /// Returns the single-token deletion repair if the token after `index`
7409    /// satisfies the failed consuming transition.
7410    fn single_token_deletion(
7411        &mut self,
7412        transition: ParserTransition<'_>,
7413        index: usize,
7414        max_token_type: i32,
7415        expected_symbols: &BTreeSet<i32>,
7416    ) -> Option<(ParserDiagnostic, usize, i32)> {
7417        let current_symbol = self.token_type_at(index);
7418        if current_symbol == TOKEN_EOF {
7419            return None;
7420        }
7421        let next_index = self.consume_index(index, current_symbol);
7422        if next_index == index {
7423            return None;
7424        }
7425        let next_symbol = self.token_type_at(next_index);
7426        if !transition.matches(next_symbol, 1, max_token_type) {
7427            return None;
7428        }
7429        let transition_expected = transition_expected_symbols(transition, max_token_type);
7430        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7431            &transition_expected
7432        } else {
7433            expected_symbols
7434        });
7435        let current = self.token_at(index);
7436        let message = format!(
7437            "extraneous input {} expecting {expected_display}",
7438            current
7439                .as_ref()
7440                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7441        );
7442        Some((
7443            diagnostic_for_token(current, message),
7444            next_index,
7445            next_symbol,
7446        ))
7447    }
7448
7449    /// Returns the repair used when deleting the current token lets a recovery
7450    /// state continue with the following token.
7451    fn current_token_deletion(
7452        &mut self,
7453        index: usize,
7454        expected_symbols: &BTreeSet<i32>,
7455    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
7456        if expected_symbols.is_empty() {
7457            return None;
7458        }
7459        let current_symbol = self.token_type_at(index);
7460        if current_symbol == TOKEN_EOF {
7461            return None;
7462        }
7463        let current = self.token_at(index);
7464        let message = format!(
7465            "extraneous input {} expecting {}",
7466            current
7467                .as_ref()
7468                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7469            self.expected_symbols_display(expected_symbols)
7470        );
7471        let diagnostic = diagnostic_for_token(current, message);
7472        let mut skipped = Vec::new();
7473        let mut cursor = index;
7474        loop {
7475            let symbol = self.token_type_at(cursor);
7476            if symbol == TOKEN_EOF {
7477                return None;
7478            }
7479            skipped.push(cursor);
7480            let next_index = self.consume_index(cursor, symbol);
7481            if next_index == cursor {
7482                return None;
7483            }
7484            let next_symbol = self.token_type_at(next_index);
7485            if expected_symbols.contains(&next_symbol) {
7486                return Some((diagnostic, next_index, skipped));
7487            }
7488            cursor = next_index;
7489        }
7490    }
7491
7492    /// Returns the single-token insertion repair for a failed consuming
7493    /// transition. The caller validates the repair by continuing from the
7494    /// transition target at the same input index.
7495    fn single_token_insertion(
7496        &mut self,
7497        transition: ParserTransition<'_>,
7498        index: usize,
7499        max_token_type: i32,
7500        expected_symbols: &BTreeSet<i32>,
7501        follow_symbols: &BTreeSet<i32>,
7502    ) -> Option<(ParserDiagnostic, i32, String)> {
7503        let current_symbol = self.token_type_at(index);
7504        if !follow_symbols.contains(&current_symbol) {
7505            return None;
7506        }
7507        let transition_expected = transition_expected_symbols(transition, max_token_type);
7508        let token_type = transition_expected.iter().next().copied()?;
7509        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
7510            &transition_expected
7511        } else {
7512            expected_symbols
7513        });
7514        let mut token_symbols = BTreeSet::new();
7515        token_symbols.insert(token_type);
7516        let missing_token_display = self.expected_symbols_display(&token_symbols);
7517        let current = self.token_at(index);
7518        let message = format!(
7519            "missing {expected_display} at {}",
7520            current
7521                .as_ref()
7522                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
7523        );
7524        let text = format!("<missing {missing_token_display}>");
7525        Some((
7526            diagnostic_for_token(current.as_ref(), message),
7527            token_type,
7528            text,
7529        ))
7530    }
7531
7532    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
7533    /// skip the unexpected current token when the following token satisfies the
7534    /// transition that failed.
7535    fn fast_single_token_deletion_recovery(
7536        &mut self,
7537        recovery: FastRecoveryRequest<'_, '_>,
7538        predicate_context: Option<FastPredicateContext<'_>>,
7539    ) -> Vec<FastRecognizeOutcome> {
7540        let FastRecoveryRequest {
7541            atn,
7542            transition,
7543            expected_symbols,
7544            target,
7545            request,
7546            visiting,
7547            memo,
7548            expected,
7549        } = recovery;
7550        let FastRecognizeRequest {
7551            stop_state,
7552            index,
7553            rule_start_index,
7554            decision_start_index,
7555            precedence,
7556            depth,
7557            ..
7558        } = request;
7559        let Some((diagnostic, next_index, next_symbol)) =
7560            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7561        else {
7562            return Vec::new();
7563        };
7564        let after_next = self.consume_index(next_index, next_symbol);
7565        let empty_recovery = self.empty_recovery_symbols();
7566        self.recognize_state_fast(
7567            atn,
7568            FastRecognizeRequest {
7569                state_number: target,
7570                stop_state,
7571                index: after_next,
7572                rule_start_index,
7573                decision_start_index,
7574                precedence,
7575                depth: depth + 1,
7576                recovery_symbols: empty_recovery,
7577                recovery_state: None,
7578            },
7579            FastRecognizeScratch {
7580                predicate_context,
7581                visiting,
7582                memo,
7583                expected,
7584            },
7585        )
7586        .into_iter()
7587        .map(|mut outcome| {
7588            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7589            outcome.diagnostics = self
7590                .recognition_arena
7591                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7592            if self.fast_token_nodes_enabled {
7593                let token = self.arena_token_node(next_index, false);
7594                self.defer_fast_outcome_node(&mut outcome, token);
7595                let error = self.arena_token_node(index, true);
7596                self.defer_fast_outcome_node(&mut outcome, error);
7597            }
7598            outcome
7599        })
7600        .collect()
7601    }
7602
7603    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
7604    /// pretend the expected transition token was present and continue without
7605    /// consuming the current token.
7606    fn fast_single_token_insertion_recovery(
7607        &mut self,
7608        recovery: FastRecoveryRequest<'_, '_>,
7609        predicate_context: Option<FastPredicateContext<'_>>,
7610    ) -> Vec<FastRecognizeOutcome> {
7611        let FastRecoveryRequest {
7612            atn,
7613            transition,
7614            expected_symbols,
7615            target,
7616            request,
7617            visiting,
7618            memo,
7619            expected,
7620        } = recovery;
7621        let FastRecognizeRequest {
7622            stop_state,
7623            index,
7624            rule_start_index,
7625            decision_start_index,
7626            precedence,
7627            depth,
7628            ..
7629        } = request;
7630        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
7631        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7632            transition,
7633            index,
7634            atn.max_token_type(),
7635            &expected_symbols,
7636            &follow_symbols,
7637        ) else {
7638            return Vec::new();
7639        };
7640        let empty_recovery = self.empty_recovery_symbols();
7641        self.recognize_state_fast(
7642            atn,
7643            FastRecognizeRequest {
7644                state_number: target,
7645                stop_state,
7646                index,
7647                rule_start_index,
7648                decision_start_index,
7649                precedence,
7650                depth: depth + 1,
7651                recovery_symbols: empty_recovery,
7652                recovery_state: None,
7653            },
7654            FastRecognizeScratch {
7655                predicate_context,
7656                visiting,
7657                memo,
7658                expected,
7659            },
7660        )
7661        .into_iter()
7662        .map(|mut outcome| {
7663            outcome.diagnostics = self
7664                .recognition_arena
7665                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7666            let missing = self.arena_missing_token_node(token_type, index, text.clone());
7667            self.defer_fast_outcome_node(&mut outcome, missing);
7668            outcome
7669        })
7670        .collect()
7671    }
7672
7673    /// Retries the current fast-recognition state after deleting one
7674    /// unexpected token that precedes a valid loop or block continuation.
7675    fn fast_current_token_deletion_recovery(
7676        &mut self,
7677        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
7678        predicate_context: Option<FastPredicateContext<'_>>,
7679    ) -> Vec<FastRecognizeOutcome> {
7680        let FastCurrentTokenDeletionRequest {
7681            atn,
7682            expected_symbols,
7683            mut request,
7684            visiting,
7685            memo,
7686            expected,
7687        } = recovery;
7688        if request.index == request.rule_start_index {
7689            return Vec::new();
7690        }
7691        let Some((diagnostic, next_index, skipped)) =
7692            self.current_token_deletion(request.index, &expected_symbols)
7693        else {
7694            return Vec::new();
7695        };
7696        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7697        request.index = next_index;
7698        request.depth += 1;
7699        request.recovery_state = None;
7700        self.recognize_state_fast(
7701            atn,
7702            request,
7703            FastRecognizeScratch {
7704                predicate_context,
7705                visiting,
7706                memo,
7707                expected,
7708            },
7709        )
7710        .into_iter()
7711        .map(|mut outcome| {
7712            outcome.diagnostics = self
7713                .recognition_arena
7714                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
7715            for index in skipped.iter().rev() {
7716                let error = self.arena_token_node(*index, true);
7717                self.defer_fast_outcome_node(&mut outcome, error);
7718            }
7719            outcome
7720        })
7721        .collect()
7722    }
7723
7724    /// Converts a failed child rule into a recovered fast-recognizer outcome so
7725    /// the parent can keep its child rule context and continue at a sync token.
7726    fn fast_child_rule_failure_recovery(
7727        &mut self,
7728        rule_index: usize,
7729        start_index: usize,
7730        sync_symbols: &BTreeSet<i32>,
7731        expected: &ExpectedTokens,
7732    ) -> Option<FastRecognizeOutcome> {
7733        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7734        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7735        let mut next_index = error_index;
7736        loop {
7737            let symbol = self.token_type_at(next_index);
7738            if sync_symbols.contains(&symbol) {
7739                if next_index == error_index {
7740                    return None;
7741                }
7742                break;
7743            }
7744            if symbol == TOKEN_EOF {
7745                break;
7746            }
7747            let after = self.consume_index(next_index, symbol);
7748            if after == next_index {
7749                break;
7750            }
7751            next_index = after;
7752        }
7753        let diagnostics = self
7754            .recognition_arena
7755            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
7756        let mut nodes = NodeSeqId::EMPTY;
7757        if self.fast_token_nodes_enabled {
7758            let error = self.arena_token_node(error_index, true);
7759            self.arena_prepend(&mut nodes, error);
7760        }
7761        Some(FastRecognizeOutcome {
7762            index: next_index,
7763            consumed_eof: false,
7764            diagnostics,
7765            deferred_nodes: FastDeferredNodeId::EMPTY,
7766            nodes,
7767        })
7768    }
7769
7770    /// Adapts the optional child-rule recovery result to the fast-recognizer
7771    /// outcome list used by rule-call transitions.
7772    fn fast_child_rule_failure_recovery_outcomes(
7773        &mut self,
7774        request: FastChildRuleFailureRecoveryRequest<'_>,
7775    ) -> Vec<FastRecognizeOutcome> {
7776        let FastChildRuleFailureRecoveryRequest {
7777            atn,
7778            rule_index,
7779            start_index,
7780            follow_state,
7781            stop_state,
7782            expected,
7783        } = request;
7784        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
7785        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
7786            .into_iter()
7787            .collect()
7788    }
7789
7790    fn defer_fast_outcome_node(
7791        &mut self,
7792        outcome: &mut FastRecognizeOutcome,
7793        node: RecognizedNodeId,
7794    ) {
7795        if outcome.deferred_nodes.is_empty() {
7796            self.arena_prepend(&mut outcome.nodes, node);
7797            return;
7798        }
7799        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
7800        let fragment = self.recognition_arena.deferred_fragment(fragment);
7801        outcome.deferred_nodes = self
7802            .recognition_arena
7803            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
7804    }
7805
7806    fn materialize_fast_deferred_nodes(
7807        &mut self,
7808        root: FastDeferredNodeId,
7809        initial_suffix: NodeSeqId,
7810    ) -> NodeSeqId {
7811        if root.is_empty() {
7812            return initial_suffix;
7813        }
7814
7815        enum Frame {
7816            Visit(FastDeferredNodeId),
7817            ContinuePrefix(FastDeferredNodeId),
7818            FinishRule {
7819                rule: FastDeferredRule,
7820                parent_suffix: NodeSeqId,
7821            },
7822        }
7823
7824        let mut result = initial_suffix;
7825        let mut pending = Vec::with_capacity(16);
7826        pending.push(Frame::Visit(root));
7827        let mut fragment_nodes = Vec::new();
7828        while let Some(frame) = pending.pop() {
7829            match frame {
7830                Frame::Visit(deferred) => {
7831                    if deferred.is_empty() {
7832                        continue;
7833                    }
7834
7835                    match self.recognition_arena.deferred_node(deferred) {
7836                        FastDeferredNode::Fragment(sequence) => {
7837                            fragment_nodes.clear();
7838                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
7839                            while let Some(node) = fragment_nodes.pop() {
7840                                self.arena_prepend(&mut result, node);
7841                            }
7842                        }
7843                        FastDeferredNode::Rule(rule) => {
7844                            let rule = self.recognition_arena.deferred_rule(rule);
7845                            let parent_suffix = result;
7846                            result = rule.children;
7847                            pending.push(Frame::FinishRule {
7848                                rule,
7849                                parent_suffix,
7850                            });
7851                            pending.push(Frame::Visit(rule.deferred_children));
7852                        }
7853                        FastDeferredNode::Concat {
7854                            prefix,
7855                            suffix: deferred_suffix,
7856                        } => {
7857                            pending.push(Frame::ContinuePrefix(prefix));
7858                            pending.push(Frame::Visit(deferred_suffix));
7859                        }
7860                    }
7861                }
7862                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
7863                Frame::FinishRule {
7864                    rule,
7865                    parent_suffix,
7866                } => {
7867                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
7868                        rule_index: rule.rule_index,
7869                        invoking_state: rule.invoking_state,
7870                        alt_number: 0,
7871                        start_index: rule.start_index,
7872                        stop_index: rule.stop_index,
7873                        return_values: None,
7874                        children: result,
7875                    });
7876                    result = parent_suffix;
7877                    self.arena_prepend(&mut result, node);
7878                }
7879            }
7880        }
7881        result
7882    }
7883
7884    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) {
7885        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
7886        outcome.nodes = self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
7887    }
7888
7889    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
7890    /// heap work instead of the native call stack.
7891    fn recognize_repetition_fast(
7892        &mut self,
7893        atn: &Atn,
7894        request: &FastRecognizeRequest,
7895        shape: FastRepetitionShape,
7896        scratch: FastRecognizeScratch<'_, '_>,
7897    ) -> Vec<FastRecognizeOutcome> {
7898        let FastRecognizeScratch {
7899            predicate_context,
7900            visiting,
7901            memo,
7902            expected,
7903        } = scratch;
7904        let lookahead = if self.fast_first_set_prefilter {
7905            atn.state(request.state_number).and_then(|state| {
7906                state
7907                    .rule_index()
7908                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
7909                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
7910            })
7911        } else {
7912            None
7913        };
7914        let mut work = Vec::with_capacity(2);
7915        push_fast_repetition_work(
7916            &mut work,
7917            shape,
7918            FastRepetitionPath {
7919                index: request.index,
7920                deferred_nodes: FastDeferredNodeId::EMPTY,
7921                diagnostics: DiagnosticSeqId::EMPTY,
7922                consumed_eof: false,
7923            },
7924            lookahead.as_deref(),
7925            self.token_type_at(request.index),
7926        );
7927        let mut coordinates = FastRepetitionCoordinates::new(request.index);
7928        let mut outcomes = Vec::new();
7929        while let Some(item) = work.pop() {
7930            match item {
7931                FastRepetitionWork::Enter(path) => {
7932                    if !coordinates.insert_entered(path) {
7933                        continue;
7934                    }
7935                    let body_outcomes = self.recognize_state_fast(
7936                        atn,
7937                        FastRecognizeRequest {
7938                            state_number: shape.enter_target,
7939                            stop_state: shape.body_stop_state,
7940                            index: path.index,
7941                            rule_start_index: request.rule_start_index,
7942                            decision_start_index: request.decision_start_index,
7943                            precedence: request.precedence,
7944                            depth: request.depth.saturating_add(1),
7945                            recovery_symbols: Rc::clone(&request.recovery_symbols),
7946                            recovery_state: request.recovery_state,
7947                        },
7948                        FastRecognizeScratch {
7949                            predicate_context,
7950                            visiting: &mut *visiting,
7951                            memo: &mut *memo,
7952                            expected: &mut *expected,
7953                        },
7954                    );
7955                    for body in body_outcomes.into_iter().rev() {
7956                        // ANTLR rejects nullable repetition bodies. Keep the
7957                        // interpreter bounded for malformed or recovered ATNs
7958                        // by mirroring the existing same-coordinate cycle cut.
7959                        if body.index <= path.index {
7960                            continue;
7961                        }
7962                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
7963                        let body_nodes = self
7964                            .recognition_arena
7965                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
7966                        let deferred_nodes = self
7967                            .recognition_arena
7968                            .concat_deferred_nodes(path.deferred_nodes, body_nodes);
7969                        let next_path = FastRepetitionPath {
7970                            index: body.index,
7971                            deferred_nodes,
7972                            diagnostics: self
7973                                .recognition_arena
7974                                .concat_diagnostics(path.diagnostics, body.diagnostics),
7975                            consumed_eof: path.consumed_eof || body.consumed_eof,
7976                        };
7977                        let symbol = self.token_type_at(next_path.index);
7978                        push_fast_repetition_work(
7979                            &mut work,
7980                            shape,
7981                            next_path,
7982                            lookahead.as_deref(),
7983                            symbol,
7984                        );
7985                    }
7986                }
7987                FastRepetitionWork::Exit(path) => {
7988                    if !coordinates.insert_exited(path) {
7989                        continue;
7990                    }
7991                    let suffixes = self.recognize_state_fast(
7992                        atn,
7993                        FastRecognizeRequest {
7994                            state_number: shape.exit_target,
7995                            stop_state: request.stop_state,
7996                            index: path.index,
7997                            rule_start_index: request.rule_start_index,
7998                            decision_start_index: request.decision_start_index,
7999                            precedence: request.precedence,
8000                            depth: request.depth.saturating_add(1),
8001                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8002                            recovery_state: request.recovery_state,
8003                        },
8004                        FastRecognizeScratch {
8005                            predicate_context,
8006                            visiting: &mut *visiting,
8007                            memo: &mut *memo,
8008                            expected: &mut *expected,
8009                        },
8010                    );
8011                    for mut outcome in suffixes {
8012                        outcome.deferred_nodes = self
8013                            .recognition_arena
8014                            .concat_deferred_nodes(path.deferred_nodes, outcome.deferred_nodes);
8015                        outcome.diagnostics = self
8016                            .recognition_arena
8017                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8018                        outcome.consumed_eof |= path.consumed_eof;
8019                        outcomes.push(outcome);
8020                    }
8021                }
8022            }
8023        }
8024        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8025        outcomes
8026    }
8027
8028    /// Attempts to reach `stop_state` from `state_number` without committing
8029    /// token consumption to the parser's public stream position.
8030    #[allow(clippy::too_many_lines)]
8031    fn recognize_state_fast(
8032        &mut self,
8033        atn: &Atn,
8034        request: FastRecognizeRequest,
8035        scratch: FastRecognizeScratch<'_, '_>,
8036    ) -> Vec<FastRecognizeOutcome> {
8037        #[cfg(feature = "perf-counters")]
8038        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8039        let FastRecognizeScratch {
8040            predicate_context,
8041            visiting,
8042            memo,
8043            expected,
8044        } = scratch;
8045        let FastRecognizeRequest {
8046            mut state_number,
8047            stop_state,
8048            mut index,
8049            rule_start_index,
8050            decision_start_index,
8051            precedence,
8052            mut depth,
8053            recovery_symbols,
8054            recovery_state,
8055        } = request;
8056        let max_token_type = atn.max_token_type();
8057        // Walk straight-line epsilon chains in a loop instead of recursing
8058        // into `recognize_state_fast` for each intermediate state. ATN
8059        // serialization places long sequences of `BasicBlock` epsilon
8060        // transitions between decisions: turning that chain into a loop
8061        // collapses many recursive calls (and their memo lookups, vec
8062        // allocations, and visit-set churn) into a single function frame.
8063        // The loop exits as soon as we hit the original state's logic
8064        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8065        // precedence) so existing fanout, recovery, and memoization still
8066        // apply unchanged.
8067        //
8068        // The inline case also handles single-atom-match states on the
8069        // happy-pass path: when the lone consuming transition matches the
8070        // current lookahead, advance the index and continue without paying
8071        // for a full `recognize_state_fast` recursion. We track tokens we
8072        // consumed inline in `inline_consumed_tokens` so they can be
8073        // prepended onto the eventual outcome list once we hit a state
8074        // whose handling falls outside this fast loop.
8075        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8076        let mut inline_consumed_eof = false;
8077        loop {
8078            if depth > RECOGNITION_DEPTH_LIMIT {
8079                return Vec::new();
8080            }
8081            if state_number == stop_state {
8082                let mut nodes = NodeSeqId::EMPTY;
8083                if self.fast_token_nodes_enabled {
8084                    for token_index in inline_consumed_tokens.iter().rev() {
8085                        let token = self.arena_token_node(*token_index, false);
8086                        self.arena_prepend(&mut nodes, token);
8087                    }
8088                }
8089                return vec![FastRecognizeOutcome {
8090                    index,
8091                    consumed_eof: inline_consumed_eof,
8092                    diagnostics: DiagnosticSeqId::EMPTY,
8093                    deferred_nodes: FastDeferredNodeId::EMPTY,
8094                    nodes,
8095                }];
8096            }
8097            let Some(state) = atn.state(state_number) else {
8098                return Vec::new();
8099            };
8100            let transitions = state.transitions();
8101            if transitions.len() == 1 && !state.precedence_rule_decision() {
8102                let transition = transitions
8103                    .first()
8104                    .expect("single transition checked above");
8105                let transition_kind = transition.kind();
8106                let target = transition.target();
8107                match transition_kind {
8108                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
8109                        if left_recursive_boundary(atn, state, target).is_none() =>
8110                    {
8111                        #[cfg(feature = "perf-counters")]
8112                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8113                        state_number = target;
8114                        depth += 1;
8115                        continue;
8116                    }
8117                    ParserTransitionKind::Predicate
8118                        if left_recursive_boundary(atn, state, target).is_none() =>
8119                    {
8120                        #[cfg(feature = "perf-counters")]
8121                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8122                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
8123                        {
8124                            record_predicate_no_viable(expected, decision_start_index, index);
8125                            return Vec::new();
8126                        }
8127                        state_number = target;
8128                        depth += 1;
8129                        continue;
8130                    }
8131                    ParserTransitionKind::Precedence
8132                        if packed_i32(transition.arg0()) >= precedence
8133                            && left_recursive_boundary(atn, state, target).is_none() =>
8134                    {
8135                        #[cfg(feature = "perf-counters")]
8136                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8137                        state_number = target;
8138                        depth += 1;
8139                        continue;
8140                    }
8141                    // Single-atom / range / set / wildcard / not-set states
8142                    // are common (~17K of ~125K calls on C#) and almost
8143                    // always succeed in pass 1: no fanout, no recovery, no
8144                    // diagnostics. Inline the token match and continue
8145                    // walking instead of recursing — the recursive path
8146                    // would just allocate a Vec, build one outcome, prepend
8147                    // a Token node, and return. Skip pass 2 (recovery
8148                    // enabled): there the failure branch matters and the
8149                    // existing recursive code records expected symbols.
8150                    ParserTransitionKind::Atom
8151                    | ParserTransitionKind::Range
8152                    | ParserTransitionKind::Set
8153                    | ParserTransitionKind::NotSet
8154                    | ParserTransitionKind::Wildcard
8155                        if !self.fast_recovery_enabled =>
8156                    {
8157                        let symbol = self.token_type_at(index);
8158                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8159                            #[cfg(feature = "perf-counters")]
8160                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8161                            if self.fast_token_nodes_enabled {
8162                                inline_consumed_tokens.push(index);
8163                            }
8164                            inline_consumed_eof |= symbol == TOKEN_EOF;
8165                            index = self.consume_index(index, symbol);
8166                            state_number = target;
8167                            depth += 1;
8168                            continue;
8169                        }
8170                        // Fall through to break and let the regular
8171                        // body handle the no-match case (returns empty).
8172                    }
8173                    _ => {}
8174                }
8175            }
8176            break;
8177        }
8178        // If we collected token nodes inline but bail to the recursive
8179        // body (decision state, rule call, etc.), the outcomes returned
8180        // below will need those token nodes prepended.
8181        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
8182        let Some(state) = atn.state(state_number) else {
8183            return Vec::new();
8184        };
8185        let transitions = state.transitions();
8186        let transition_count = transitions.len();
8187        if !self.fast_recovery_enabled
8188            && let Some(shape) = fast_repetition_shape(atn, state)
8189        {
8190            let mut outcomes = self.recognize_repetition_fast(
8191                atn,
8192                &FastRecognizeRequest {
8193                    state_number,
8194                    stop_state,
8195                    index,
8196                    rule_start_index,
8197                    decision_start_index,
8198                    precedence,
8199                    depth,
8200                    recovery_symbols: Rc::clone(&recovery_symbols),
8201                    recovery_state,
8202                },
8203                shape,
8204                FastRecognizeScratch {
8205                    predicate_context,
8206                    visiting: &mut *visiting,
8207                    memo: &mut *memo,
8208                    expected: &mut *expected,
8209                },
8210            );
8211            if inline_pending {
8212                for outcome in &mut outcomes {
8213                    outcome.consumed_eof |= inline_consumed_eof;
8214                    if self.fast_token_nodes_enabled {
8215                        for token_index in inline_consumed_tokens.iter().rev() {
8216                            let token = self.arena_token_node(*token_index, false);
8217                            self.defer_fast_outcome_node(outcome, token);
8218                        }
8219                    }
8220                }
8221            }
8222            return outcomes;
8223        }
8224        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
8225        // fields and the rule/decision boundary indices are pure plumbing —
8226        // they only affect the recovery branch and the no-viable diagnostic
8227        // recording, neither of which fires when recovery is off. Zeroing
8228        // them in the memo key collapses calls that visit the same
8229        // `(state, index)` from different rule-call sites onto one cache
8230        // entry, which is the dominant cost on large grammars (e.g. C#) where
8231        // many rules eventually delegate into the same `expression` /
8232        // `primary_expression` / `type` branches.
8233        let key = if self.fast_recovery_enabled {
8234            FastRecognizeKey {
8235                state_number,
8236                stop_state,
8237                index,
8238                rule_start_index,
8239                decision_start_index,
8240                precedence,
8241                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
8242                recovery_state,
8243            }
8244        } else {
8245            FastRecognizeKey {
8246                state_number,
8247                stop_state,
8248                index,
8249                rule_start_index: 0,
8250                decision_start_index: None,
8251                precedence,
8252                recovery_symbols_id: 0,
8253                recovery_state: None,
8254            }
8255        };
8256        // Once the clean-pass probe has established that coordinates do not
8257        // repeat, stop paying for the full memo table. Recovery always keeps
8258        // memoization because cached failures carry diagnostics, while
8259        // repeat-heavy clean parses promote before reaching sparse mode.
8260        let memo_lookup_enabled = self.fast_recovery_enabled
8261            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
8262        if memo_lookup_enabled {
8263            if let Some(outcomes) = memo.get(&key) {
8264                #[cfg(feature = "perf-counters")]
8265                {
8266                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
8267                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
8268                }
8269                // Materialize a fresh `Vec` from the cached slice; the caller
8270                // mutates per-outcome state (eof flags, prepended nodes) so we
8271                // can't hand them the shared backing.
8272                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
8273                    let inline_eof = inline_consumed_eof;
8274                    let inline_tokens = &inline_consumed_tokens;
8275                    return outcomes
8276                        .iter()
8277                        .copied()
8278                        .map(|mut outcome| {
8279                            if inline_eof {
8280                                outcome.consumed_eof = true;
8281                            }
8282                            if self.fast_token_nodes_enabled {
8283                                for token_index in inline_tokens.iter().rev() {
8284                                    let token = self.arena_token_node(*token_index, false);
8285                                    self.defer_fast_outcome_node(&mut outcome, token);
8286                                }
8287                            }
8288                            outcome
8289                        })
8290                        .collect();
8291                }
8292                return outcomes.to_vec();
8293            }
8294            #[cfg(feature = "perf-counters")]
8295            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
8296        }
8297
8298        // Cycle detection: clean recognition keeps the narrow static cycle
8299        // guard used on hot paths. Recovery needs the broader epsilon-state
8300        // guard because an otherwise non-nullable loop body can recover as an
8301        // empty child at EOF and re-enter the loop at the same token.
8302        let needs_cycle_guard = if self.fast_recovery_enabled {
8303            transitions.iter().any(ParserTransition::is_epsilon)
8304        } else {
8305            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
8306        };
8307        #[cfg(feature = "perf-counters")]
8308        if needs_cycle_guard {
8309            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
8310        } else {
8311            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
8312            match state
8313                .transitions()
8314                .first()
8315                .expect("single-transition path requires one transition")
8316                .data()
8317            {
8318                Transition::Rule { .. } => {
8319                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
8320                }
8321                Transition::Atom { .. }
8322                | Transition::Range { .. }
8323                | Transition::Set { .. }
8324                | Transition::NotSet { .. }
8325                | Transition::Wildcard { .. } => {
8326                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
8327                }
8328                _ => {
8329                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
8330                }
8331            }
8332        }
8333        let has_inserted_cycle_guard = if needs_cycle_guard {
8334            if !visiting.insert(key.clone()) {
8335                #[cfg(feature = "perf-counters")]
8336                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
8337                return Vec::new();
8338            }
8339            true
8340        } else {
8341            false
8342        };
8343        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
8344            Some(index)
8345        } else {
8346            decision_start_index
8347        };
8348        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
8349            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
8350        } else {
8351            (Rc::clone(&recovery_symbols), recovery_state)
8352        };
8353
8354        // Lookahead-based pruning. At a multi-alternative state we cache the
8355        // look-1 set of every outgoing transition; on visit we keep only the
8356        // transitions whose look-1 can accept the current lookahead (or that
8357        // can be reached without consuming and so could legitimately match a
8358        // shorter input). This is the main speedup vs. blind speculative
8359        // recursion: it lets each visit fan out only to the alternatives that
8360        // could possibly contribute a clean parse, mirroring the SLL phase of
8361        // ANTLR's adaptive prediction.
8362        //
8363        // Pruning is skipped at:
8364        //   * rule-start states (a child rule call may need every internal
8365        //     transition to surface single-token recovery diagnostics that
8366        //     ANTLR's reference parser emits at the rule's first consuming
8367        //     transition; the FIRST-set retry path turns the prefilter off
8368        //     entirely so let's keep this lightweight too),
8369        //   * left-recursive precedence loops (the precedence transition's
8370        //     gating is dynamic),
8371        //   * states with too few alternatives to benefit.
8372        let lookahead_filter = if transition_count > 1
8373            && self.fast_first_set_prefilter
8374            && !state.precedence_rule_decision()
8375            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
8376        {
8377            state
8378                .rule_index()
8379                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8380                .map(|rule_stop| {
8381                    let symbol = self.token_type_at(index);
8382                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
8383                    (symbol, entry)
8384                })
8385        } else {
8386            None
8387        };
8388        // LL(1) fast path: when the FIRST sets for the decision are disjoint
8389        // and none is nullable, the lookahead deterministically selects one
8390        // alternative. The recursive recognizer can then commit to that single
8391        // alt without iterating every transition through `should_skip_via_lookahead`
8392        // — saving (transition_count - 1) filter probes per visit.
8393        //
8394        // Result is cached per `(state, lookahead_token)` on the parser
8395        // instance, so subsequent visits skip the FIRST-set scan entirely.
8396        let ll1_only_alt: Option<usize> = if transition_count > 1
8397            && let Some((symbol, entry)) = lookahead_filter.as_ref()
8398        {
8399            let key = (state.state_number(), *symbol);
8400            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
8401                cached
8402            } else {
8403                let result = ll1_unique_alt(entry, *symbol);
8404                self.ll1_decision_cache.insert(key, result);
8405                result
8406            }
8407        } else {
8408            None
8409        };
8410        let lookahead_filter = lookahead_filter.as_ref();
8411        // Pre-size only when we expect at least one outcome to land — most
8412        // single-transition fall-throughs (the loop above didn't catch
8413        // because they're atom/rule/predicate) push at most one entry, so
8414        // reserving one slot avoids a reallocation while keeping the
8415        // unused-slot waste at one element.
8416        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
8417        for (transition_index, transition) in transitions.iter().enumerate() {
8418            if let Some(alt) = ll1_only_alt {
8419                // LL(1) determinism: skip every alt except the chosen one.
8420                if alt != transition_index {
8421                    continue;
8422                }
8423            }
8424            let transition_kind = transition.kind();
8425            if ll1_only_alt.is_none()
8426                && should_skip_via_lookahead(
8427                    transition_kind,
8428                    transition_index,
8429                    lookahead_filter,
8430                    index,
8431                    self.fast_recovery_enabled,
8432                    expected,
8433                )
8434            {
8435                continue;
8436            }
8437            let target = transition.target();
8438            match transition_kind {
8439                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
8440                    #[cfg(feature = "perf-counters")]
8441                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8442                    let boundary = left_recursive_boundary(atn, state, target);
8443                    outcomes.extend(
8444                        self.recognize_state_fast(
8445                            atn,
8446                            FastRecognizeRequest {
8447                                state_number: target,
8448                                stop_state,
8449                                index,
8450                                rule_start_index,
8451                                decision_start_index: next_decision_start_index,
8452                                precedence,
8453                                depth: depth + 1,
8454                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8455                                recovery_state: epsilon_recovery_state,
8456                            },
8457                            FastRecognizeScratch {
8458                                predicate_context,
8459                                visiting,
8460                                memo,
8461                                expected,
8462                            },
8463                        )
8464                        .into_iter()
8465                        .map(|mut outcome| {
8466                            if let Some(rule_index) = boundary {
8467                                let boundary = self.arena_boundary_node(rule_index);
8468                                self.defer_fast_outcome_node(&mut outcome, boundary);
8469                            }
8470                            outcome
8471                        }),
8472                    );
8473                }
8474                ParserTransitionKind::Predicate => {
8475                    #[cfg(feature = "perf-counters")]
8476                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8477                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
8478                        let boundary = left_recursive_boundary(atn, state, target);
8479                        outcomes.extend(
8480                            self.recognize_state_fast(
8481                                atn,
8482                                FastRecognizeRequest {
8483                                    state_number: target,
8484                                    stop_state,
8485                                    index,
8486                                    rule_start_index,
8487                                    decision_start_index: next_decision_start_index,
8488                                    precedence,
8489                                    depth: depth + 1,
8490                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8491                                    recovery_state: epsilon_recovery_state,
8492                                },
8493                                FastRecognizeScratch {
8494                                    predicate_context,
8495                                    visiting,
8496                                    memo,
8497                                    expected,
8498                                },
8499                            )
8500                            .into_iter()
8501                            .map(|mut outcome| {
8502                                if let Some(rule_index) = boundary {
8503                                    let boundary = self.arena_boundary_node(rule_index);
8504                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8505                                }
8506                                outcome
8507                            }),
8508                        );
8509                    } else {
8510                        record_predicate_no_viable(expected, next_decision_start_index, index);
8511                    }
8512                }
8513                ParserTransitionKind::Precedence => {
8514                    let transition_precedence = packed_i32(transition.arg0());
8515                    if transition_precedence >= precedence {
8516                        let boundary = left_recursive_boundary(atn, state, target);
8517                        outcomes.extend(
8518                            self.recognize_state_fast(
8519                                atn,
8520                                FastRecognizeRequest {
8521                                    state_number: target,
8522                                    stop_state,
8523                                    index,
8524                                    rule_start_index,
8525                                    decision_start_index: next_decision_start_index,
8526                                    precedence,
8527                                    depth: depth + 1,
8528                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8529                                    recovery_state: epsilon_recovery_state,
8530                                },
8531                                FastRecognizeScratch {
8532                                    predicate_context,
8533                                    visiting,
8534                                    memo,
8535                                    expected,
8536                                },
8537                            )
8538                            .into_iter()
8539                            .map(|mut outcome| {
8540                                if let Some(rule_index) = boundary {
8541                                    let boundary = self.arena_boundary_node(rule_index);
8542                                    self.defer_fast_outcome_node(&mut outcome, boundary);
8543                                }
8544                                outcome
8545                            }),
8546                        );
8547                    }
8548                }
8549                ParserTransitionKind::Rule => {
8550                    let rule_index = transition.arg0() as usize;
8551                    let follow_state = transition.arg1() as usize;
8552                    let rule_precedence = packed_i32(transition.arg2());
8553                    #[cfg(feature = "perf-counters")]
8554                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
8555                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
8556                        continue;
8557                    };
8558                    // Lookahead-based pruning. The recognizer would otherwise
8559                    // explore every speculative rule call, producing exponential
8560                    // work on grammars with many epsilon-reachable rules. When
8561                    // the rule is non-nullable and its FIRST set excludes the
8562                    // current lookahead, recursion can't find a clean path
8563                    // *through this rule*. Skipping is only safe if some sibling
8564                    // transition can still consume the lookahead — otherwise the
8565                    // rule call is the sole continuation and must run so the
8566                    // single-token insertion / deletion recovery inside the
8567                    // called rule can fire (mirroring ANTLR's reference behavior
8568                    // of conjuring a missing token at child-rule entry).
8569                    let symbol = self.token_type_at(index);
8570                    if self.fast_first_set_prefilter {
8571                        // Probe the shared cross-parse cache first; build
8572                        // the entry on miss and intern it there. The
8573                        // computation is purely a function of the ATN, so
8574                        // the cached entry is reused across parses (and
8575                        // freshly-instantiated parser values that share
8576                        // the same `&'static Atn`).
8577                        //
8578                        // `rule_first_set` returns the computed entry
8579                        // directly — it intentionally skips inserting into
8580                        // the cache when the FIRST-set walk hit a cycle, so
8581                        // we cannot assume the entry is in the cache after
8582                        // computing it.
8583                        let first = self.cached_rule_first_set(atn, target, child_stop);
8584                        if should_skip_rule_via_first_set(
8585                            &first,
8586                            symbol,
8587                            self.fast_recovery_enabled,
8588                            index,
8589                            expected,
8590                        ) {
8591                            continue;
8592                        }
8593                    }
8594                    let expected_before_child =
8595                        self.fast_recovery_enabled.then(|| expected.clone());
8596                    let mut children = self.recognize_state_fast(
8597                        atn,
8598                        FastRecognizeRequest {
8599                            state_number: target,
8600                            stop_state: child_stop,
8601                            index,
8602                            rule_start_index: index,
8603                            decision_start_index: None,
8604                            precedence: rule_precedence,
8605                            depth: depth + 1,
8606                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
8607                            recovery_state: epsilon_recovery_state,
8608                        },
8609                        FastRecognizeScratch {
8610                            predicate_context,
8611                            visiting,
8612                            memo,
8613                            expected,
8614                        },
8615                    );
8616                    if children.is_empty() && self.fast_recovery_enabled {
8617                        children = self.fast_child_rule_failure_recovery_outcomes(
8618                            FastChildRuleFailureRecoveryRequest {
8619                                atn,
8620                                rule_index,
8621                                start_index: index,
8622                                follow_state,
8623                                stop_state,
8624                                expected,
8625                            },
8626                        );
8627                    }
8628                    if let Some(expected_before_child) = expected_before_child {
8629                        if children
8630                            .iter()
8631                            .any(|child| child.diagnostics.is_empty() && child.index > index)
8632                        {
8633                            *expected = expected_before_child;
8634                        }
8635                    }
8636                    for child in children {
8637                        let child_index = child.index;
8638                        let child_consumed_eof = child.consumed_eof;
8639                        let child_diagnostics = child.diagnostics;
8640                        let empty_recovery = self.empty_recovery_symbols();
8641                        let follow_outcomes = self.recognize_state_fast(
8642                            atn,
8643                            FastRecognizeRequest {
8644                                state_number: follow_state,
8645                                stop_state,
8646                                index: child_index,
8647                                rule_start_index,
8648                                decision_start_index: next_decision_start_index,
8649                                precedence,
8650                                depth: depth + 1,
8651                                recovery_symbols: empty_recovery,
8652                                recovery_state: None,
8653                            },
8654                            FastRecognizeScratch {
8655                                predicate_context,
8656                                visiting,
8657                                memo,
8658                                expected,
8659                            },
8660                        );
8661                        if follow_outcomes.is_empty() {
8662                            continue;
8663                        }
8664                        let child_stop_index =
8665                            self.rule_stop_token_index(child_index, child_consumed_eof);
8666                        let child_node = self.build_parse_trees.then(|| {
8667                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
8668                                rule_index: u32::try_from(rule_index)
8669                                    .expect("rule index fits in u32"),
8670                                invoking_state: i32::try_from(invoking_state_number(state_number))
8671                                    .expect("invoking state fits in i32"),
8672                                start_index: u32::try_from(index)
8673                                    .expect("rule start index fits in u32"),
8674                                stop_index: child_stop_index.map(|stop_index| {
8675                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
8676                                }),
8677                                deferred_children: child.deferred_nodes,
8678                                children: child.nodes,
8679                            })
8680                        });
8681                        let child_diags_empty = child_diagnostics.is_empty();
8682                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
8683                            outcome.consumed_eof |= child_consumed_eof;
8684                            // Skip the prepend dance when there's nothing to
8685                            // merge from the child — common case in pass 1.
8686                            if !child_diags_empty {
8687                                outcome.diagnostics = self
8688                                    .recognition_arena
8689                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
8690                            }
8691                            if let Some(child_node) = child_node {
8692                                outcome.deferred_nodes = self
8693                                    .recognition_arena
8694                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
8695                            }
8696                            outcome
8697                        }));
8698                    }
8699                }
8700                ParserTransitionKind::Atom
8701                | ParserTransitionKind::Range
8702                | ParserTransitionKind::Set
8703                | ParserTransitionKind::NotSet
8704                | ParserTransitionKind::Wildcard => {
8705                    #[cfg(feature = "perf-counters")]
8706                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8707                    let symbol = self.token_type_at(index);
8708                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8709                        let next_index = self.consume_index(index, symbol);
8710                        let empty_recovery = self.empty_recovery_symbols();
8711                        outcomes.extend(
8712                            self.recognize_state_fast(
8713                                atn,
8714                                FastRecognizeRequest {
8715                                    state_number: target,
8716                                    stop_state,
8717                                    index: next_index,
8718                                    rule_start_index,
8719                                    decision_start_index: next_decision_start_index,
8720                                    precedence,
8721                                    depth: depth + 1,
8722                                    recovery_symbols: empty_recovery,
8723                                    recovery_state: None,
8724                                },
8725                                FastRecognizeScratch {
8726                                    predicate_context,
8727                                    visiting,
8728                                    memo,
8729                                    expected,
8730                                },
8731                            )
8732                            .into_iter()
8733                            .map(|mut outcome| {
8734                                outcome.consumed_eof |= symbol == TOKEN_EOF;
8735                                if self.fast_token_nodes_enabled {
8736                                    let token = self.arena_token_node(index, false);
8737                                    self.defer_fast_outcome_node(&mut outcome, token);
8738                                }
8739                                outcome
8740                            }),
8741                        );
8742                    } else {
8743                        if !self.fast_recovery_enabled {
8744                            // In pass 1 there is no recovery to attempt; the
8745                            // recovery branch below would never run, and the
8746                            // `expected_symbols` computation is just there
8747                            // to gate that branch. Skipping it eliminates
8748                            // ~1× `state_expected_symbols` lookup per failed
8749                            // atom transition (≈82K on mono-statement.cs)
8750                            // for zero observable behavior change.
8751                            continue;
8752                        }
8753                        let expected_symbols = fast_recovery_expected_symbols(
8754                            self,
8755                            atn,
8756                            state.state_number(),
8757                            &recovery_symbols,
8758                        );
8759                        if expected_symbols.contains(&symbol) {
8760                            continue;
8761                        }
8762                        {
8763                            expected.record_transition(index, transition, max_token_type);
8764                            record_no_viable_if_ambiguous(
8765                                expected,
8766                                next_decision_start_index,
8767                                index,
8768                            );
8769                            outcomes.extend(self.fast_single_token_deletion_recovery(
8770                                FastRecoveryRequest {
8771                                    atn,
8772                                    transition,
8773                                    expected_symbols: Rc::clone(&expected_symbols),
8774                                    target,
8775                                    request: FastRecognizeRequest {
8776                                        state_number,
8777                                        stop_state,
8778                                        index,
8779                                        rule_start_index,
8780                                        decision_start_index,
8781                                        precedence,
8782                                        depth,
8783                                        recovery_symbols: Rc::clone(&recovery_symbols),
8784                                        recovery_state,
8785                                    },
8786                                    visiting,
8787                                    memo,
8788                                    expected,
8789                                },
8790                                predicate_context,
8791                            ));
8792                            if !state_is_left_recursive_rule(atn, state) {
8793                                outcomes.extend(self.fast_single_token_insertion_recovery(
8794                                    FastRecoveryRequest {
8795                                        atn,
8796                                        transition,
8797                                        expected_symbols: Rc::clone(&expected_symbols),
8798                                        target,
8799                                        request: FastRecognizeRequest {
8800                                            state_number,
8801                                            stop_state,
8802                                            index,
8803                                            rule_start_index,
8804                                            decision_start_index,
8805                                            precedence,
8806                                            depth,
8807                                            recovery_symbols: Rc::clone(&recovery_symbols),
8808                                            recovery_state,
8809                                        },
8810                                        visiting,
8811                                        memo,
8812                                        expected,
8813                                    },
8814                                    predicate_context,
8815                                ));
8816                            }
8817                            outcomes.extend(self.fast_current_token_deletion_recovery(
8818                                FastCurrentTokenDeletionRequest {
8819                                    atn,
8820                                    expected_symbols,
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                        }
8839                    }
8840                }
8841            }
8842        }
8843
8844        if has_inserted_cycle_guard {
8845            visiting.remove(&key);
8846        }
8847        if matches!(
8848            self.prediction_mode,
8849            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
8850        ) && self.fast_recovery_enabled
8851        {
8852            // Without recovery enabled every outcome already has empty
8853            // diagnostics, so the discard pass is a no-op — skipping it
8854            // saves an iter+retain on each of the ~1M visits.
8855            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
8856        }
8857        if self.fast_recovery_enabled {
8858            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
8859        } else {
8860            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8861        }
8862        // Skip memoization for single-transition states whose outcome is
8863        // unambiguous: they only get re-entered if the caller revisits the
8864        // exact same call site, which is rare since the loop above already
8865        // collapsed straight-line epsilon walks. Multi-alternative states
8866        // are where backtracking actually revisits the same coordinate, so
8867        // we still memoize there. With recovery on we keep the existing
8868        // memoization unconditionally because the recovery branch may
8869        // record diagnostics that the cache must surface to repeated
8870        // failed visits.
8871        let should_memoize = self.fast_recovery_enabled
8872            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
8873        // Apply inline pending state to each outcome before returning.
8874        // Tokens consumed inline by the loop-collapse don't appear in the
8875        // recursive recognizer's output, so we need to prepend them here.
8876        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
8877            if inline_consumed_eof {
8878                outcome.consumed_eof = true;
8879            }
8880            if !inline_consumed_tokens.is_empty() {
8881                for token_index in inline_consumed_tokens.iter().rev() {
8882                    let token = self.arena_token_node(*token_index, false);
8883                    self.defer_fast_outcome_node(&mut outcome, token);
8884                }
8885            }
8886            outcome
8887        };
8888        if should_memoize {
8889            #[cfg(feature = "perf-counters")]
8890            {
8891                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
8892                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
8893                match outcomes.len() {
8894                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8895                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8896                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8897                }
8898            }
8899            // The memo is keyed by the loop-exit `(state_number, index)` so
8900            // the inline-consumed tokens belong to *this* call's output, not
8901            // the cached result. Memoize the bare outcomes (without the
8902            // inline-pending data), then prepend the inline data on return.
8903            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
8904            memo.insert(key, Rc::clone(&stored));
8905            if inline_pending {
8906                return stored
8907                    .iter()
8908                    .copied()
8909                    .map(&mut apply_inline_pending)
8910                    .collect();
8911            }
8912            return stored.to_vec();
8913        }
8914        #[cfg(feature = "perf-counters")]
8915        match outcomes.len() {
8916            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
8917            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
8918            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
8919        }
8920        if inline_pending {
8921            return outcomes.into_iter().map(apply_inline_pending).collect();
8922        }
8923        outcomes
8924    }
8925
8926    /// Explores single-token deletion recovery while preserving the matched
8927    /// token and skipped error token in the selected parse tree path.
8928    fn single_token_deletion_recovery(
8929        &mut self,
8930        recovery: RecoveryRequest<'_, '_>,
8931    ) -> Vec<RecognizeOutcome> {
8932        let RecoveryRequest {
8933            atn,
8934            transition,
8935            expected_symbols,
8936            target,
8937            request,
8938            visiting,
8939            memo,
8940            expected,
8941        } = recovery;
8942        let RecognizeRequest {
8943            stop_state,
8944            index,
8945            rule_start_index,
8946            decision_start_index,
8947            init_action_rules,
8948            predicates,
8949            semantics,
8950            rule_args,
8951            member_actions,
8952            return_actions,
8953            local_int_arg,
8954            member_values,
8955            return_values,
8956            rule_alt_number,
8957            track_alt_numbers,
8958            consumed_eof,
8959            precedence,
8960            depth,
8961            ..
8962        } = request;
8963        let Some((diagnostic, next_index, next_symbol)) =
8964            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8965        else {
8966            return Vec::new();
8967        };
8968        let after_next = self.consume_index(next_index, next_symbol);
8969        self.recognize_state(
8970            atn,
8971            RecognizeRequest {
8972                state_number: target,
8973                stop_state,
8974                index: after_next,
8975                rule_start_index,
8976                decision_start_index,
8977                init_action_rules,
8978                predicates,
8979                semantics,
8980                rule_args,
8981                member_actions,
8982                return_actions,
8983                local_int_arg,
8984                member_values,
8985                return_values,
8986                rule_alt_number,
8987                track_alt_numbers,
8988                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
8989                precedence,
8990                depth: depth + 1,
8991                recovery_symbols: BTreeSet::new(),
8992                recovery_state: None,
8993            },
8994            visiting,
8995            memo,
8996            expected,
8997        )
8998        .into_iter()
8999        .map(|mut outcome| {
9000            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9001            outcome.diagnostics = self
9002                .recognition_arena
9003                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9004            let token = self.arena_token_node(next_index, false);
9005            self.arena_prepend(&mut outcome.nodes, token);
9006            let error = self.arena_token_node(index, true);
9007            self.arena_prepend(&mut outcome.nodes, error);
9008            outcome
9009        })
9010        .collect()
9011    }
9012
9013    /// Retries the current recognition state after deleting one unexpected
9014    /// token, preserving the deleted token as an error node in the parse tree.
9015    fn current_token_deletion_recovery(
9016        &mut self,
9017        recovery: CurrentTokenDeletionRequest<'_, '_>,
9018    ) -> Vec<RecognizeOutcome> {
9019        let CurrentTokenDeletionRequest {
9020            atn,
9021            expected_symbols,
9022            mut request,
9023            visiting,
9024            memo,
9025            expected,
9026        } = recovery;
9027        let error_index = request.index;
9028        if error_index == request.rule_start_index {
9029            return Vec::new();
9030        }
9031        let Some((diagnostic, next_index, skipped)) =
9032            self.current_token_deletion(error_index, &expected_symbols)
9033        else {
9034            return Vec::new();
9035        };
9036        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9037        request.index = next_index;
9038        request.depth += 1;
9039        request.recovery_state = None;
9040        self.recognize_state(atn, request, visiting, memo, expected)
9041            .into_iter()
9042            .map(|mut outcome| {
9043                outcome.diagnostics = self
9044                    .recognition_arena
9045                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9046                for index in skipped.iter().rev() {
9047                    let error = self.arena_token_node(*index, true);
9048                    self.arena_prepend(&mut outcome.nodes, error);
9049                }
9050                outcome
9051            })
9052            .collect()
9053    }
9054
9055    /// Falls back after deletion/insertion repairs cannot continue from a
9056    /// failed consuming transition.
9057    fn consuming_failure_fallback(
9058        &mut self,
9059        fallback: ConsumingFailureFallback<'_>,
9060        visiting: &mut BTreeSet<RecognizeKey>,
9061        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9062        expected: &mut ExpectedTokens,
9063    ) -> Vec<RecognizeOutcome> {
9064        if fallback.expected_symbols.is_empty() {
9065            return Vec::new();
9066        }
9067        if fallback.symbol == TOKEN_EOF {
9068            return self.eof_consuming_failure_fallback(fallback, expected);
9069        }
9070        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9071    }
9072
9073    /// Keeps unexpected non-EOF input visible as an error node when no repair
9074    /// path can otherwise reach the transition target.
9075    fn non_eof_consuming_failure_fallback(
9076        &mut self,
9077        fallback: ConsumingFailureFallback<'_>,
9078        visiting: &mut BTreeSet<RecognizeKey>,
9079        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9080        expected: &mut ExpectedTokens,
9081    ) -> Vec<RecognizeOutcome> {
9082        let ConsumingFailureFallback {
9083            atn,
9084            target,
9085            request,
9086            symbol,
9087            expected_symbols,
9088            decision_start_index,
9089            decision,
9090        } = fallback;
9091        let error_index = request.index;
9092        let diagnostic =
9093            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
9094        let next_index = self.consume_index(error_index, symbol);
9095        self.recognize_state(
9096            atn,
9097            RecognizeRequest {
9098                state_number: target,
9099                stop_state: request.stop_state,
9100                index: next_index,
9101                rule_start_index: request.rule_start_index,
9102                decision_start_index,
9103                init_action_rules: request.init_action_rules,
9104                predicates: request.predicates,
9105                semantics: request.semantics,
9106                rule_args: request.rule_args,
9107                member_actions: request.member_actions,
9108                return_actions: request.return_actions,
9109                local_int_arg: request.local_int_arg,
9110                member_values: request.member_values,
9111                return_values: request.return_values,
9112                rule_alt_number: request.rule_alt_number,
9113                track_alt_numbers: request.track_alt_numbers,
9114                consumed_eof: request.consumed_eof,
9115                precedence: request.precedence,
9116                depth: request.depth + 1,
9117                recovery_symbols: BTreeSet::new(),
9118                recovery_state: None,
9119            },
9120            visiting,
9121            memo,
9122            expected,
9123        )
9124        .into_iter()
9125        .map(|mut outcome| {
9126            prepend_decision(&mut outcome, decision);
9127            outcome.diagnostics = self
9128                .recognition_arena
9129                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9130            let error = self.arena_token_node(error_index, true);
9131            self.arena_prepend(&mut outcome.nodes, error);
9132            outcome
9133        })
9134        .collect()
9135    }
9136
9137    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
9138    /// behavior of unwinding instead of inserting caller tokens at EOF.
9139    fn eof_consuming_failure_fallback(
9140        &mut self,
9141        fallback: ConsumingFailureFallback<'_>,
9142        expected: &ExpectedTokens,
9143    ) -> Vec<RecognizeOutcome> {
9144        let request = fallback.request;
9145        if request.index == request.rule_start_index {
9146            return Vec::new();
9147        }
9148        let diagnostic =
9149            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
9150        let diagnostics = self
9151            .recognition_arena
9152            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
9153        vec![RecognizeOutcome {
9154            index: request.index,
9155            consumed_eof: request.consumed_eof,
9156            alt_number: request.rule_alt_number,
9157            member_values: request.member_values,
9158            return_values: request.return_values,
9159            diagnostics,
9160            decisions: Vec::new(),
9161            actions: Vec::new(),
9162            nodes: NodeSeqId::EMPTY,
9163        }]
9164    }
9165
9166    /// Explores single-token insertion recovery while adding a conjured
9167    /// missing-token error node to the selected parse tree path.
9168    fn single_token_insertion_recovery(
9169        &mut self,
9170        recovery: RecoveryRequest<'_, '_>,
9171    ) -> Vec<RecognizeOutcome> {
9172        let RecoveryRequest {
9173            atn,
9174            transition,
9175            expected_symbols,
9176            target,
9177            request,
9178            visiting,
9179            memo,
9180            expected,
9181        } = recovery;
9182        let RecognizeRequest {
9183            stop_state,
9184            index,
9185            rule_start_index,
9186            decision_start_index,
9187            init_action_rules,
9188            predicates,
9189            semantics,
9190            rule_args,
9191            member_actions,
9192            return_actions,
9193            local_int_arg,
9194            member_values,
9195            return_values,
9196            rule_alt_number,
9197            track_alt_numbers,
9198            consumed_eof,
9199            precedence,
9200            depth,
9201            ..
9202        } = request;
9203        let follow_symbols = state_expected_symbols(atn, transition.target());
9204        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
9205            transition,
9206            index,
9207            atn.max_token_type(),
9208            &expected_symbols,
9209            &follow_symbols,
9210        ) else {
9211            return Vec::new();
9212        };
9213        self.recognize_state(
9214            atn,
9215            RecognizeRequest {
9216                state_number: target,
9217                stop_state,
9218                index,
9219                rule_start_index,
9220                decision_start_index,
9221                init_action_rules,
9222                predicates,
9223                semantics,
9224                rule_args,
9225                member_actions,
9226                return_actions,
9227                local_int_arg,
9228                member_values,
9229                return_values,
9230                rule_alt_number,
9231                track_alt_numbers,
9232                consumed_eof,
9233                precedence,
9234                depth: depth + 1,
9235                recovery_symbols: BTreeSet::new(),
9236                recovery_state: None,
9237            },
9238            visiting,
9239            memo,
9240            expected,
9241        )
9242        .into_iter()
9243        .map(|mut outcome| {
9244            outcome.diagnostics = self
9245                .recognition_arena
9246                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9247            let missing = self.arena_missing_token_node(token_type, index, text.clone());
9248            self.arena_prepend(&mut outcome.nodes, missing);
9249            outcome
9250        })
9251        .collect()
9252    }
9253
9254    /// Attempts to reach `stop_state` and carries semantic actions for the
9255    /// selected parser path.
9256    #[allow(clippy::too_many_lines)]
9257    fn recognize_state(
9258        &mut self,
9259        atn: &Atn,
9260        request: RecognizeRequest<'_>,
9261        visiting: &mut BTreeSet<RecognizeKey>,
9262        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9263        expected: &mut ExpectedTokens,
9264    ) -> Vec<RecognizeOutcome> {
9265        let request_template = request.clone();
9266        let RecognizeRequest {
9267            state_number,
9268            stop_state,
9269            index,
9270            rule_start_index,
9271            decision_start_index,
9272            init_action_rules,
9273            predicates,
9274            semantics,
9275            rule_args,
9276            member_actions,
9277            return_actions,
9278            local_int_arg,
9279            member_values,
9280            return_values,
9281            rule_alt_number,
9282            track_alt_numbers,
9283            consumed_eof,
9284            precedence,
9285            depth,
9286            recovery_symbols,
9287            recovery_state,
9288        } = request;
9289        if depth > RECOGNITION_DEPTH_LIMIT {
9290            return Vec::new();
9291        }
9292        if state_number == stop_state {
9293            return stop_outcome(
9294                index,
9295                consumed_eof,
9296                rule_alt_number,
9297                member_values,
9298                return_values,
9299            );
9300        }
9301        let key = RecognizeKey {
9302            state_number,
9303            stop_state,
9304            index,
9305            rule_start_index,
9306            decision_start_index,
9307            local_int_arg,
9308            member_values: member_values.clone(),
9309            return_values: return_values.clone(),
9310            rule_alt_number,
9311            track_alt_numbers,
9312            consumed_eof,
9313            precedence,
9314            recovery_symbols: recovery_symbols.clone(),
9315            recovery_state,
9316        };
9317        if let Some(outcomes) = memo.get(&key) {
9318            return outcomes.clone();
9319        }
9320
9321        let visit_key = key.clone();
9322        if !visiting.insert(visit_key.clone()) {
9323            return Vec::new();
9324        }
9325
9326        let Some(state) = atn.state(state_number) else {
9327            visiting.remove(&visit_key);
9328            return Vec::new();
9329        };
9330        let transitions = state.transitions();
9331        let transition_count = transitions.len();
9332        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9333            Some(index)
9334        } else {
9335            decision_start_index
9336        };
9337        let (epsilon_recovery_symbols, epsilon_recovery_state) =
9338            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
9339        let mut outcomes = Vec::new();
9340        for (transition_index, transition) in transitions.iter().enumerate() {
9341            let decision =
9342                transition_decision(atn, state, transition_count, transition_index, predicates);
9343            let next_alt_number = next_alt_number(
9344                state,
9345                transition_count,
9346                transition_index,
9347                rule_alt_number,
9348                track_alt_numbers,
9349            );
9350            let transition_data = transition.data();
9351            match &transition_data {
9352                Transition::Epsilon { target } | Transition::Action { target, .. } => {
9353                    let action_rule_index = match &transition_data {
9354                        Transition::Action { rule_index, .. } => Some(*rule_index),
9355                        _ => None,
9356                    };
9357                    outcomes.extend(self.recognize_epsilon_or_action_step(
9358                        atn,
9359                        &request_template,
9360                        EpsilonActionStep {
9361                            source_state: state_number,
9362                            target: *target,
9363                            action_rule_index,
9364                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
9365                            decision,
9366                            decision_start_index: next_decision_start_index,
9367                            alt_number: next_alt_number,
9368                            recovery_symbols: epsilon_recovery_symbols.clone(),
9369                            recovery_state: epsilon_recovery_state,
9370                        },
9371                        RecognizeScratch {
9372                            visiting,
9373                            memo,
9374                            expected,
9375                        },
9376                    ));
9377                }
9378                Transition::Predicate {
9379                    target,
9380                    rule_index,
9381                    pred_index,
9382                    ..
9383                } => {
9384                    let predicate = PredicateEval {
9385                        index,
9386                        rule_index: *rule_index,
9387                        pred_index: *pred_index,
9388                        predicates,
9389                        semantics,
9390                        context: None,
9391                        local_int_arg,
9392                        member_values: &member_values,
9393                    };
9394                    if self.parser_predicate_matches(predicate) {
9395                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
9396                        outcomes.extend(
9397                            self.recognize_state(
9398                                atn,
9399                                RecognizeRequest {
9400                                    state_number: *target,
9401                                    stop_state,
9402                                    index,
9403                                    rule_start_index,
9404                                    decision_start_index: next_decision_start_index,
9405                                    init_action_rules,
9406                                    predicates,
9407                                    semantics,
9408                                    rule_args,
9409                                    member_actions,
9410                                    return_actions,
9411                                    local_int_arg,
9412                                    member_values: member_values.clone(),
9413                                    return_values: return_values.clone(),
9414                                    rule_alt_number: next_alt_number,
9415                                    track_alt_numbers,
9416                                    consumed_eof,
9417                                    precedence,
9418                                    depth: depth + 1,
9419                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9420                                    recovery_state: epsilon_recovery_state,
9421                                },
9422                                visiting,
9423                                memo,
9424                                expected,
9425                            )
9426                            .into_iter()
9427                            .map(|mut outcome| {
9428                                prepend_decision(&mut outcome, decision);
9429                                if let Some(rule_index) = left_recursive_boundary {
9430                                    let boundary = self.arena_boundary_node(rule_index);
9431                                    self.arena_prepend(&mut outcome.nodes, boundary);
9432                                }
9433                                outcome
9434                            }),
9435                        );
9436                    } else if let Some(message) = semantics
9437                        .and_then(|semantics| {
9438                            self.parser_semantic_ir_predicate_failure_message(
9439                                *rule_index,
9440                                *pred_index,
9441                                semantics,
9442                            )
9443                        })
9444                        .or_else(|| {
9445                            self.parser_predicate_failure_message(
9446                                *rule_index,
9447                                *pred_index,
9448                                predicates,
9449                            )
9450                        })
9451                    {
9452                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
9453                            rule_index: *rule_index,
9454                            index,
9455                            message,
9456                            member_values: member_values.clone(),
9457                            return_values: return_values.clone(),
9458                            rule_alt_number,
9459                        }));
9460                    } else {
9461                        record_predicate_no_viable(expected, next_decision_start_index, index);
9462                    }
9463                }
9464                Transition::Precedence {
9465                    target,
9466                    precedence: transition_precedence,
9467                } => {
9468                    if *transition_precedence >= precedence {
9469                        outcomes.extend(
9470                            self.recognize_state(
9471                                atn,
9472                                RecognizeRequest {
9473                                    state_number: *target,
9474                                    stop_state,
9475                                    index,
9476                                    rule_start_index,
9477                                    decision_start_index: next_decision_start_index,
9478                                    init_action_rules,
9479                                    predicates,
9480                                    semantics,
9481                                    rule_args,
9482                                    member_actions,
9483                                    return_actions,
9484                                    local_int_arg,
9485                                    member_values: member_values.clone(),
9486                                    return_values: return_values.clone(),
9487                                    rule_alt_number: next_alt_number,
9488                                    track_alt_numbers,
9489                                    consumed_eof,
9490                                    precedence,
9491                                    depth: depth + 1,
9492                                    recovery_symbols: epsilon_recovery_symbols.clone(),
9493                                    recovery_state: epsilon_recovery_state,
9494                                },
9495                                visiting,
9496                                memo,
9497                                expected,
9498                            )
9499                            .into_iter()
9500                            .map(|mut outcome| {
9501                                prepend_decision(&mut outcome, decision);
9502                                outcome
9503                            }),
9504                        );
9505                    }
9506                }
9507                Transition::Rule {
9508                    target,
9509                    rule_index,
9510                    follow_state,
9511                    precedence: rule_precedence,
9512                    ..
9513                } => {
9514                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
9515                        continue;
9516                    };
9517                    let child_local_int_arg =
9518                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
9519                    let expected_before_child = expected.clone();
9520                    let children = self.recognize_state(
9521                        atn,
9522                        RecognizeRequest {
9523                            state_number: *target,
9524                            stop_state: child_stop,
9525                            index,
9526                            rule_start_index: index,
9527                            decision_start_index: None,
9528                            init_action_rules,
9529                            predicates,
9530                            semantics,
9531                            rule_args,
9532                            member_actions,
9533                            return_actions,
9534                            local_int_arg: child_local_int_arg,
9535                            member_values: member_values.clone(),
9536                            return_values: BTreeMap::new(),
9537                            rule_alt_number: 0,
9538                            track_alt_numbers,
9539                            consumed_eof: false,
9540                            precedence: *rule_precedence,
9541                            depth: depth + 1,
9542                            recovery_symbols: epsilon_recovery_symbols.clone(),
9543                            recovery_state: epsilon_recovery_state,
9544                        },
9545                        visiting,
9546                        memo,
9547                        expected,
9548                    );
9549                    let children = if children.is_empty() {
9550                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
9551                            atn,
9552                            rule_index: *rule_index,
9553                            start_index: index,
9554                            follow_state: *follow_state,
9555                            stop_state,
9556                            member_values: member_values.clone(),
9557                            expected,
9558                        })
9559                    } else {
9560                        children
9561                    };
9562                    let preserve_child_expected =
9563                        self.child_expected_reaches_clean_eof(&children, expected);
9564                    restore_expected(
9565                        &children,
9566                        index,
9567                        expected,
9568                        expected_before_child,
9569                        preserve_child_expected,
9570                    );
9571                    for child in children {
9572                        let child_stop_index =
9573                            self.rule_stop_token_index(child.index, child.consumed_eof);
9574                        let child_nodes = self
9575                            .recognition_arena
9576                            .fold_left_recursive_boundaries(child.nodes);
9577                        let child_node = self.arena_rule_node(ArenaRuleSpec {
9578                            rule_index: *rule_index,
9579                            invoking_state: invoking_state_number(state_number),
9580                            alt_number: child.alt_number,
9581                            start_index: index,
9582                            stop_index: child_stop_index,
9583                            return_values: child.return_values.clone(),
9584                            children: child_nodes,
9585                        });
9586                        outcomes.extend(
9587                            self.recognize_state(
9588                                atn,
9589                                RecognizeRequest {
9590                                    state_number: *follow_state,
9591                                    stop_state,
9592                                    index: child.index,
9593                                    rule_start_index,
9594                                    decision_start_index: next_decision_start_index,
9595                                    init_action_rules,
9596                                    predicates,
9597                                    semantics,
9598                                    rule_args,
9599                                    member_actions,
9600                                    return_actions,
9601                                    local_int_arg,
9602                                    member_values: child.member_values.clone(),
9603                                    return_values: return_values.clone(),
9604                                    rule_alt_number,
9605                                    track_alt_numbers,
9606                                    consumed_eof: consumed_eof || child.consumed_eof,
9607                                    precedence,
9608                                    depth: depth + 1,
9609                                    recovery_symbols: BTreeSet::new(),
9610                                    recovery_state: None,
9611                                },
9612                                visiting,
9613                                memo,
9614                                expected,
9615                            )
9616                            .into_iter()
9617                            .map(|mut outcome| {
9618                                outcome.consumed_eof |= child.consumed_eof;
9619                                outcome.diagnostics = self
9620                                    .recognition_arena
9621                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
9622                                let mut decisions = child.decisions.clone();
9623                                decisions.append(&mut outcome.decisions);
9624                                outcome.decisions = decisions;
9625                                prepend_decision(&mut outcome, decision);
9626                                let mut actions = child.actions.clone();
9627                                if init_action_rules.contains(rule_index) {
9628                                    actions.insert(
9629                                        0,
9630                                        ParserAction::new_rule_init(
9631                                            *rule_index,
9632                                            index,
9633                                            Some(*follow_state),
9634                                        ),
9635                                    );
9636                                }
9637                                actions.append(&mut outcome.actions);
9638                                outcome.actions = actions;
9639                                self.arena_prepend(&mut outcome.nodes, child_node);
9640                                outcome
9641                            }),
9642                        );
9643                    }
9644                }
9645                Transition::Atom { target, .. }
9646                | Transition::Range { target, .. }
9647                | Transition::Set { target, .. }
9648                | Transition::NotSet { target, .. }
9649                | Transition::Wildcard { target, .. } => {
9650                    let symbol = self.token_type_at(index);
9651                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
9652                        let next_index = self.consume_index(index, symbol);
9653                        outcomes.extend(
9654                            self.recognize_state(
9655                                atn,
9656                                RecognizeRequest {
9657                                    state_number: *target,
9658                                    stop_state,
9659                                    index: next_index,
9660                                    rule_start_index,
9661                                    decision_start_index: next_decision_start_index,
9662                                    init_action_rules,
9663                                    predicates,
9664                                    semantics,
9665                                    rule_args,
9666                                    member_actions,
9667                                    return_actions,
9668                                    local_int_arg,
9669                                    member_values: member_values.clone(),
9670                                    return_values: return_values.clone(),
9671                                    rule_alt_number: next_alt_number,
9672                                    track_alt_numbers,
9673                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
9674                                    precedence,
9675                                    depth: depth + 1,
9676                                    recovery_symbols: BTreeSet::new(),
9677                                    recovery_state: None,
9678                                },
9679                                visiting,
9680                                memo,
9681                                expected,
9682                            )
9683                            .into_iter()
9684                            .map(|mut outcome| {
9685                                prepend_decision(&mut outcome, decision);
9686                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9687                                let token = self.arena_token_node(index, false);
9688                                self.arena_prepend(&mut outcome.nodes, token);
9689                                outcome
9690                            }),
9691                        );
9692                    } else {
9693                        let expected_symbols =
9694                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
9695                        if expected_symbols.contains(&symbol) {
9696                            continue;
9697                        }
9698                        expected.record_transition(index, transition, atn.max_token_type());
9699                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
9700                        let before_recovery = outcomes.len();
9701                        let recovery_request = request_template.clone();
9702                        outcomes.extend(
9703                            self.single_token_deletion_recovery(RecoveryRequest {
9704                                atn,
9705                                transition,
9706                                expected_symbols: expected_symbols.clone(),
9707                                target: *target,
9708                                request: recovery_request.clone(),
9709                                visiting,
9710                                memo,
9711                                expected,
9712                            })
9713                            .into_iter()
9714                            .map(|mut outcome| {
9715                                prepend_decision(&mut outcome, decision);
9716                                outcome
9717                            }),
9718                        );
9719                        if !state_is_left_recursive_rule(atn, state) {
9720                            outcomes.extend(
9721                                self.single_token_insertion_recovery(RecoveryRequest {
9722                                    atn,
9723                                    transition,
9724                                    expected_symbols: expected_symbols.clone(),
9725                                    target: *target,
9726                                    request: recovery_request.clone(),
9727                                    visiting,
9728                                    memo,
9729                                    expected,
9730                                })
9731                                .into_iter()
9732                                .map(|mut outcome| {
9733                                    prepend_decision(&mut outcome, decision);
9734                                    outcome
9735                                }),
9736                            );
9737                        }
9738                        outcomes.extend(self.current_token_deletion_recovery(
9739                            CurrentTokenDeletionRequest {
9740                                atn,
9741                                expected_symbols: expected_symbols.clone(),
9742                                request: recovery_request.clone(),
9743                                visiting,
9744                                memo,
9745                                expected,
9746                            },
9747                        ));
9748                        if outcomes.len() == before_recovery {
9749                            outcomes.extend(self.consuming_failure_fallback(
9750                                ConsumingFailureFallback {
9751                                    atn,
9752                                    target: *target,
9753                                    request: recovery_request,
9754                                    symbol,
9755                                    expected_symbols,
9756                                    decision_start_index: next_decision_start_index,
9757                                    decision,
9758                                },
9759                                visiting,
9760                                memo,
9761                                expected,
9762                            ));
9763                        }
9764                    }
9765                }
9766            }
9767        }
9768
9769        visiting.remove(&visit_key);
9770        self.record_prediction_diagnostics(atn, state, index, &outcomes);
9771        if matches!(
9772            self.prediction_mode,
9773            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9774        ) {
9775            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
9776        }
9777        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
9778        memo.insert(key, outcomes.clone());
9779        outcomes
9780    }
9781
9782    /// Follows an epsilon or semantic-action transition while preserving the
9783    /// path-local side effects that may later become generated action output.
9784    fn recognize_epsilon_or_action_step(
9785        &mut self,
9786        atn: &Atn,
9787        request: &RecognizeRequest<'_>,
9788        step: EpsilonActionStep,
9789        scratch: RecognizeScratch<'_>,
9790    ) -> Vec<RecognizeOutcome> {
9791        let RecognizeScratch {
9792            visiting,
9793            memo,
9794            expected,
9795        } = scratch;
9796        let action = step.action_rule_index.map(|rule_index| {
9797            ParserAction::new(
9798                step.source_state,
9799                rule_index,
9800                request.rule_start_index,
9801                self.rule_stop_token_index(request.index, request.consumed_eof),
9802            )
9803        });
9804        let next_member_values = if action.is_some() {
9805            member_values_after_action(
9806                step.source_state,
9807                request.member_actions,
9808                request.semantics,
9809                &request.member_values,
9810            )
9811        } else {
9812            request.member_values.clone()
9813        };
9814        let next_return_values = action.map_or_else(
9815            || request.return_values.clone(),
9816            |action| {
9817                return_values_after_action(
9818                    step.source_state,
9819                    action.rule_index(),
9820                    request.return_actions,
9821                    request.semantics,
9822                    &request.return_values,
9823                )
9824            },
9825        );
9826
9827        self.recognize_state(
9828            atn,
9829            RecognizeRequest {
9830                state_number: step.target,
9831                stop_state: request.stop_state,
9832                index: request.index,
9833                rule_start_index: request.rule_start_index,
9834                decision_start_index: step.decision_start_index,
9835                init_action_rules: request.init_action_rules,
9836                predicates: request.predicates,
9837                semantics: request.semantics,
9838                rule_args: request.rule_args,
9839                member_actions: request.member_actions,
9840                return_actions: request.return_actions,
9841                local_int_arg: request.local_int_arg,
9842                member_values: next_member_values,
9843                return_values: next_return_values,
9844                rule_alt_number: step.alt_number,
9845                track_alt_numbers: request.track_alt_numbers,
9846                consumed_eof: request.consumed_eof,
9847                precedence: request.precedence,
9848                depth: request.depth + 1,
9849                recovery_symbols: step.recovery_symbols,
9850                recovery_state: step.recovery_state,
9851            },
9852            visiting,
9853            memo,
9854            expected,
9855        )
9856        .into_iter()
9857        .map(|mut outcome| {
9858            prepend_decision(&mut outcome, step.decision);
9859            if let Some(rule_index) = step.left_recursive_boundary {
9860                let boundary = self.arena_boundary_node(rule_index);
9861                self.arena_prepend(&mut outcome.nodes, boundary);
9862            }
9863            if let Some(action) = action {
9864                outcome.actions.insert(0, action);
9865            }
9866            outcome
9867        })
9868        .collect()
9869    }
9870
9871    /// Reads the token type at an absolute token-stream index without moving
9872    /// the parser's stream cursor. The fast recognizer probes lookahead at
9873    /// every state visit, so avoiding the seek round-trip is a measurable
9874    /// hot-path win on long inputs.
9875    fn token_type_at(&mut self, index: usize) -> i32 {
9876        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
9877            self.input.fill();
9878        }
9879        self.input.token_type_at_index(index)
9880    }
9881
9882    /// Returns the cached `state_expected_symbols` set for an ATN state.
9883    ///
9884    /// The fast recognizer consults this set on every state visit through
9885    /// `next_recovery_context`; the underlying DFS is a pure function of the
9886    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
9887    ///
9888    /// Caching is layered through `intern_recovery_symbols` so two ATN states
9889    /// with the same expected-symbol set share one `Rc`. That invariant is
9890    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
9891    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
9892    /// always interned before it ends up in a key.
9893    fn cached_state_expected_symbols(
9894        &mut self,
9895        atn: &Atn,
9896        state_number: usize,
9897    ) -> Rc<BTreeSet<i32>> {
9898        if let Some(cached) = self.state_expected_cache.get(&state_number) {
9899            return Rc::clone(cached);
9900        }
9901        let symbols = state_expected_symbols(atn, state_number);
9902        let entry = self.intern_recovery_symbols(symbols);
9903        self.state_expected_cache
9904            .insert(state_number, Rc::clone(&entry));
9905        entry
9906    }
9907
9908    fn cached_state_expected_token_set(
9909        &mut self,
9910        atn: &Atn,
9911        state_number: usize,
9912    ) -> Rc<TokenBitSet> {
9913        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
9914            return Rc::clone(cached);
9915        }
9916        // Purely a function of the ATN, so back the per-parser cache with the
9917        // thread-shared one — fresh parser instances (one per parse in
9918        // generated usage) start warm instead of rewalking the ATN.
9919        let symbols = with_shared_atn_caches(atn, |cache| {
9920            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
9921                return Rc::clone(cached);
9922            }
9923            let symbols = Rc::new(state_expected_token_set(atn, state_number));
9924            cache
9925                .state_expected_tokens
9926                .insert(state_number, Rc::clone(&symbols));
9927            symbols
9928        });
9929        self.state_expected_token_cache
9930            .insert(state_number, Rc::clone(&symbols));
9931        symbols
9932    }
9933
9934    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
9935        if self.rule_stop_reach_cache.len() <= state_number {
9936            self.rule_stop_reach_cache
9937                .resize_with(atn.states().len().max(state_number + 1), || None);
9938        }
9939        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
9940            return reaches;
9941        }
9942        let reaches = with_shared_atn_caches(atn, |cache| {
9943            *cache
9944                .rule_stop_reach
9945                .entry(state_number)
9946                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
9947        });
9948        self.rule_stop_reach_cache[state_number] = Some(reaches);
9949        reaches
9950    }
9951
9952    /// Returns the parser's empty `recovery_symbols` singleton so callers can
9953    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
9954    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
9955        Rc::clone(&self.empty_recovery_symbols)
9956    }
9957
9958    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
9959    /// recognizer can hash and compare keys by pointer.
9960    ///
9961    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
9962    /// come from this method or the empty singleton; otherwise two
9963    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
9964    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
9965    /// recognition coordinates.
9966    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
9967        if set.is_empty() {
9968            return Rc::clone(&self.empty_recovery_symbols);
9969        }
9970        let candidate = Rc::new(set);
9971        match self.recovery_symbols_intern.get(&candidate) {
9972            Some(existing) => Rc::clone(existing),
9973            None => {
9974                self.recovery_symbols_intern
9975                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
9976                candidate
9977            }
9978        }
9979    }
9980
9981    /// Returns the cached look-1 entry for a decision state, computing it on
9982    /// first use. Multi-alternative states are visited many times during
9983    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
9984    /// hash lookup per visit.
9985    fn cached_decision_lookahead(
9986        &mut self,
9987        atn: &Atn,
9988        state: AtnState<'_>,
9989        rule_stop_state: usize,
9990    ) -> Rc<DecisionLookahead> {
9991        // Hit the parser-instance cache first. Decision lookahead is purely
9992        // a function of the ATN/state, so on a warm cache we skip the
9993        // thread-local + RefCell + HashMap-entry dance through
9994        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
9995        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
9996        // hashmap-entry overhead in profiles.
9997        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
9998            return Rc::clone(cached);
9999        }
10000        let entry = with_shared_atn_caches(atn, |cache| {
10001            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10002                return Rc::clone(cached);
10003            }
10004            let mut entry = DecisionLookahead {
10005                transitions: Vec::with_capacity(state.transitions().len()),
10006            };
10007            for transition in &state.transitions() {
10008                entry.transitions.push(transition_first_set(
10009                    atn,
10010                    transition,
10011                    rule_stop_state,
10012                    &mut cache.first_set,
10013                ));
10014            }
10015            let entry = Rc::new(entry);
10016            cache
10017                .decision_lookahead
10018                .insert(state.state_number(), Rc::clone(&entry));
10019            entry
10020        });
10021        self.decision_lookahead_cache
10022            .insert(state.state_number(), Rc::clone(&entry));
10023        entry
10024    }
10025
10026    fn cached_rule_first_set(
10027        &mut self,
10028        atn: &Atn,
10029        target: usize,
10030        child_stop: usize,
10031    ) -> Rc<FirstSet> {
10032        if self.rule_first_set_cache.len() <= target {
10033            self.rule_first_set_cache
10034                .resize_with(atn.states().len().max(target + 1), || None);
10035        }
10036        if let Some(cached) = self
10037            .rule_first_set_cache
10038            .get(target)
10039            .and_then(Option::as_ref)
10040        {
10041            return Rc::clone(cached);
10042        }
10043        let first = with_shared_first_set_cache(atn, |cache| {
10044            rule_first_set(atn, target, child_stop, cache)
10045        });
10046        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
10047        first
10048    }
10049
10050    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
10051        let atn_key = SharedAtnCacheKey::for_atn(atn);
10052        if self.empty_cycle_cache_atn != Some(atn_key) {
10053            self.empty_cycle_cache.clear();
10054            self.empty_cycle_cache_atn = Some(atn_key);
10055        }
10056        if self.empty_cycle_cache.len() <= state_number {
10057            self.empty_cycle_cache
10058                .resize_with(atn.state_count().max(state_number + 1), || None);
10059        }
10060        if let Some(cached) = self.empty_cycle_cache[state_number] {
10061            return cached;
10062        }
10063        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
10064        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
10065        self.empty_cycle_cache[state_number] = Some(result);
10066        result
10067    }
10068
10069    fn empty_path_reaches_state(
10070        &mut self,
10071        atn: &Atn,
10072        state_number: usize,
10073        target_state: usize,
10074        visited: &mut FxHashSet<usize>,
10075    ) -> bool {
10076        if !visited.insert(state_number) {
10077            return false;
10078        }
10079        let Some(state) = atn.state(state_number) else {
10080            return false;
10081        };
10082        for transition in &state.transitions() {
10083            let kind = transition.kind();
10084            let target = transition.target();
10085            match kind {
10086                ParserTransitionKind::Atom
10087                | ParserTransitionKind::Range
10088                | ParserTransitionKind::Set
10089                | ParserTransitionKind::NotSet
10090                | ParserTransitionKind::Wildcard => {}
10091                ParserTransitionKind::Rule => {
10092                    let rule_index = transition.arg0() as usize;
10093                    let follow_state = transition.arg1() as usize;
10094                    if target == target_state
10095                        || self.empty_path_reaches_state(atn, target, target_state, visited)
10096                    {
10097                        return true;
10098                    }
10099                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
10100                        continue;
10101                    };
10102                    if self.cached_rule_first_set(atn, target, child_stop).nullable
10103                        && (follow_state == target_state
10104                            || self.empty_path_reaches_state(
10105                                atn,
10106                                follow_state,
10107                                target_state,
10108                                visited,
10109                            ))
10110                    {
10111                        return true;
10112                    }
10113                }
10114                ParserTransitionKind::Epsilon
10115                | ParserTransitionKind::Predicate
10116                | ParserTransitionKind::Action
10117                | ParserTransitionKind::Precedence => {
10118                    if target == target_state
10119                        || self.empty_path_reaches_state(atn, target, target_state, visited)
10120                    {
10121                        return true;
10122                    }
10123                }
10124            }
10125        }
10126        false
10127    }
10128
10129    /// Decides whether the clean recognizer should use its full outcome memo
10130    /// table for this coordinate.
10131    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
10132        match self.clean_memo_mode {
10133            CleanMemoMode::Promote => true,
10134            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
10135            CleanMemoMode::Sparse => {
10136                self.clean_memo_sparse_samples += 1;
10137                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
10138                    return false;
10139                }
10140                self.clean_memo_sparse_samples = 0;
10141                self.clean_memo_mode = CleanMemoMode::Probe;
10142                self.clean_memo_probe_samples = 0;
10143                self.clean_memo_probe_repeats = 0;
10144                self.clean_memo_probe_seen.clear();
10145                self.observe_clean_memo_probe(key)
10146            }
10147        }
10148    }
10149
10150    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
10151        self.clean_memo_probe_samples += 1;
10152        if !self.clean_memo_probe_seen.insert(key.clone()) {
10153            self.clean_memo_probe_repeats += 1;
10154        }
10155        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
10156            self.clean_memo_mode = CleanMemoMode::Promote;
10157            self.clean_memo_probe_seen.clear();
10158            return true;
10159        }
10160        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
10161            self.clean_memo_mode = CleanMemoMode::Sparse;
10162            self.clean_memo_sparse_samples = 0;
10163            self.clean_memo_probe_seen.clear();
10164            return false;
10165        }
10166        true
10167    }
10168
10169    /// Borrows the visible token at an absolute token-stream index.
10170    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
10171        self.input.get(index)
10172    }
10173
10174    /// Returns the compact token ID at an absolute token-stream index.
10175    fn token_id_at(&self, index: usize) -> Option<TokenId> {
10176        self.input.get_id(index)
10177    }
10178
10179    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
10180        let token = self
10181            .token_id_at(index)
10182            .expect("recognized token index must exist in the token store");
10183        let node = if error {
10184            ArenaRecognizedNode::ErrorToken { token }
10185        } else {
10186            ArenaRecognizedNode::Token { token }
10187        };
10188        self.recognition_arena.push_node(node)
10189    }
10190
10191    fn arena_missing_token_node(
10192        &mut self,
10193        token_type: i32,
10194        at_index: usize,
10195        text: String,
10196    ) -> RecognizedNodeId {
10197        let extra = self
10198            .recognition_arena
10199            .push_extra(RecognitionExtra::MissingToken {
10200                token_type,
10201                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
10202                text,
10203            });
10204        self.recognition_arena
10205            .push_node(ArenaRecognizedNode::MissingToken { extra })
10206    }
10207
10208    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
10209        let ArenaRuleSpec {
10210            rule_index,
10211            invoking_state,
10212            alt_number,
10213            start_index,
10214            stop_index,
10215            return_values,
10216            children,
10217        } = spec;
10218        let return_values = (!return_values.is_empty()).then(|| {
10219            self.recognition_arena
10220                .push_extra(RecognitionExtra::ReturnValues(return_values))
10221        });
10222        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
10223            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10224            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
10225            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
10226            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
10227            stop_index: stop_index
10228                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
10229            return_values,
10230            children,
10231        })
10232    }
10233
10234    fn arena_boundary_node(&mut self, rule_index: usize) -> RecognizedNodeId {
10235        self.recognition_arena
10236            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
10237                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
10238            })
10239    }
10240
10241    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
10242        *sequence = self.recognition_arena.prepend(*sequence, node);
10243    }
10244
10245    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
10246        self.last_recognition_arena_root = root;
10247        self.last_recognition_arena_diagnostics = diagnostics;
10248        #[cfg(feature = "perf-counters")]
10249        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
10250            let stats = self.recognition_arena_stats();
10251            #[allow(clippy::print_stderr)]
10252            {
10253                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
10254                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
10255                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
10256                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
10257                eprintln!("perf recognition_links_total={}", stats.total_links);
10258                eprintln!("perf recognition_links_live={}", stats.live_links);
10259                eprintln!("perf recognition_links_dead={}", stats.dead_links);
10260                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
10261                eprintln!("perf recognition_extras_total={}", stats.total_extras);
10262                eprintln!("perf recognition_extras_live={}", stats.live_extras);
10263                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
10264                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
10265            }
10266        }
10267    }
10268
10269    fn reset_recognition_arena(&mut self) {
10270        self.recognition_arena.reset();
10271        self.last_recognition_arena_root = NodeSeqId::EMPTY;
10272        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
10273    }
10274
10275    /// Normalizes the current token-stream cursor to the next parser-visible
10276    /// token before capturing a rule start boundary.
10277    fn current_visible_index(&mut self) -> usize {
10278        let index = self.input.index();
10279        self.input.seek(index);
10280        self.input.index()
10281    }
10282
10283    /// Reports whether a child rule reached EOF cleanly while also recording
10284    /// an EOF expectation from a longer path inside that child.
10285    fn child_expected_reaches_clean_eof(
10286        &mut self,
10287        children: &[RecognizeOutcome],
10288        expected: &ExpectedTokens,
10289    ) -> bool {
10290        let Some(index) = expected.index else {
10291            return false;
10292        };
10293        self.token_type_at(index) == TOKEN_EOF
10294            && children
10295                .iter()
10296                .any(|child| child.diagnostics.is_empty() && child.index == index)
10297    }
10298
10299    /// Finds the previous token visible to the parser before `index`.
10300    ///
10301    /// The token stream cursor skips hidden-channel tokens, so subtracting one
10302    /// from a visible-token index can point at whitespace. Parser intervals use
10303    /// this helper to stop at the previous visible token while preserving hidden
10304    /// text inside the rendered interval.
10305    fn previous_token_index(&self, index: usize) -> Option<usize> {
10306        self.input.previous_visible_token_index(index)
10307    }
10308
10309    /// Returns the token-stream index used as a rule stop boundary.
10310    ///
10311    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
10312    /// stop at `index` rather than at the previous visible token.
10313    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
10314        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
10315            Some(index)
10316        } else {
10317            self.previous_token_index(index)
10318        }
10319    }
10320
10321    /// Stop-token index for a rule's `@after` action, matching the boundary that
10322    /// `finish_rule` records on the rule context.
10323    ///
10324    /// A rule that matched EOF leaves the cursor parked on the EOF token
10325    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
10326    /// the current index rather than the previous visible token. Without this,
10327    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
10328    /// report the token before EOF (or `None` for empty input), diverging from
10329    /// the rule context that `finish_rule` builds.
10330    ///
10331    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
10332    /// rule ends right before EOF without matching it (the cursor is parked on
10333    /// EOF, but the rule did not consume it). Prefer
10334    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
10335    /// rule context already recorded with the real flag. Kept for callers without
10336    /// the rule tree in hand.
10337    #[must_use]
10338    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
10339        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
10340        self.rule_stop_token_index(current_index, consumed_eof)
10341    }
10342
10343    /// Stop-token index for a rule's `@after` action, taken from the stop token
10344    /// the rule context already recorded.
10345    ///
10346    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
10347    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
10348    /// the rule context — even when the rule ends immediately before EOF without
10349    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
10350    /// to the cursor-based inference only when the tree carries no rule stop.
10351    #[must_use]
10352    pub fn after_action_stop_index_for_tree(
10353        &mut self,
10354        tree: ParseTree,
10355        current_index: usize,
10356    ) -> Option<usize> {
10357        if let Some(stop) = self
10358            .node(tree)
10359            .as_rule()
10360            .and_then(crate::tree::RuleNodeView::stop_id)
10361        {
10362            return Some(stop.index());
10363        }
10364        self.after_action_stop_index(current_index)
10365    }
10366
10367    /// Start-token index for a rule's `@after` action, taken from the start token
10368    /// the rule context already recorded.
10369    ///
10370    /// `enter_rule` sets the rule context start to the first visible token (it
10371    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
10372    /// `$text` in an `@after` action aligned with the rule context — even when the
10373    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
10374    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
10375    /// the tree carries no rule start.
10376    #[must_use]
10377    pub fn after_action_start_index_for_tree(
10378        &self,
10379        tree: ParseTree,
10380        fallback_index: usize,
10381    ) -> usize {
10382        if let Some(start) = self
10383            .node(tree)
10384            .as_rule()
10385            .and_then(crate::tree::RuleNodeView::start_id)
10386        {
10387            return start.index();
10388        }
10389        fallback_index
10390    }
10391
10392    /// Returns the rule stop token for a selected parse path.
10393    ///
10394    /// EOF transitions do not advance the token-stream cursor, so an EOF match
10395    /// must use the current token rather than the previous visible token.
10396    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
10397        self.rule_stop_token_index(index, consumed_eof)
10398            .and_then(|token_index| self.token_id_at(token_index))
10399    }
10400
10401    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
10402    ///
10403    /// Generated Java reports the failed-predicate message at the current
10404    /// lookahead, then consumes until rule recovery can resume. The metadata
10405    /// runtime models the same visible tree shape by keeping skipped tokens as
10406    /// error nodes and returning from the active rule at EOF.
10407    fn predicate_failure_recovery(
10408        &mut self,
10409        request: PredicateFailureRecovery<'_>,
10410    ) -> RecognizeOutcome {
10411        let PredicateFailureRecovery {
10412            rule_index,
10413            index,
10414            message,
10415            member_values,
10416            return_values,
10417            rule_alt_number,
10418        } = request;
10419        let rule_name = self
10420            .rule_names()
10421            .get(rule_index)
10422            .map_or_else(|| rule_index.to_string(), Clone::clone);
10423        let diagnostic = diagnostic_for_token(
10424            self.token_at(index).as_ref(),
10425            format!("rule {rule_name} {message}"),
10426        );
10427        let mut reversed_nodes = NodeSeqId::EMPTY;
10428        let mut next_index = index;
10429        loop {
10430            let symbol = self.token_type_at(next_index);
10431            if symbol == TOKEN_EOF {
10432                break;
10433            }
10434            let error = self.arena_token_node(next_index, true);
10435            self.arena_prepend(&mut reversed_nodes, error);
10436            let after = self.consume_index(next_index, symbol);
10437            if after == next_index {
10438                break;
10439            }
10440            next_index = after;
10441        }
10442        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
10443        let diagnostics = self
10444            .recognition_arena
10445            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10446        RecognizeOutcome {
10447            index: next_index,
10448            consumed_eof: false,
10449            alt_number: rule_alt_number,
10450            member_values,
10451            return_values,
10452            diagnostics,
10453            decisions: Vec::new(),
10454            actions: Vec::new(),
10455            nodes,
10456        }
10457    }
10458
10459    /// Evaluates a user hook for a predicate coordinate that has no generated
10460    /// runtime table entry.
10461    fn parser_semantic_hook_result(
10462        &mut self,
10463        request: ParserSemanticHookRequest<'_>,
10464    ) -> Option<bool> {
10465        let ParserSemanticHookRequest {
10466            index,
10467            rule_index,
10468            pred_index,
10469            context,
10470            local_int_arg,
10471            member_values,
10472        } = request;
10473        let rule_name = self.rule_names().get(rule_index).cloned();
10474        self.input.seek(index);
10475        let input = &mut self.input;
10476        let semantic_hooks = &mut self.semantic_hooks;
10477        let mut ctx = ParserSemCtx {
10478            input,
10479            tree_storage: &self.tree,
10480            rule_index,
10481            coordinate_index: pred_index,
10482            rule_name,
10483            context,
10484            tree: None,
10485            local_int_arg,
10486            member_values,
10487            action: None,
10488        };
10489        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
10490    }
10491
10492    /// Re-inserts unknown-predicate coordinates recorded before a nested
10493    /// interpreted recognition, preserving order and skipping any the nested
10494    /// call already recorded, so a generated parent's fail-loud coordinates
10495    /// survive descending into an interpreted child.
10496    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
10497        if prior.is_empty() {
10498            return;
10499        }
10500        let mut merged = prior;
10501        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
10502            if !merged.contains(&coordinate) {
10503                merged.push(coordinate);
10504            }
10505        }
10506        self.unknown_predicate_hits = merged;
10507    }
10508
10509    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
10510    /// that has no entry in the generated predicate table.
10511    ///
10512    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
10513    /// the guarded path is abandoned; the parse entry surfaces the recorded
10514    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
10515    /// because a parse that consulted an unknown predicate is unreliable no
10516    /// matter which paths were ultimately selected.
10517    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
10518        apply_unknown_predicate_policy(
10519            self.unknown_predicate_policy,
10520            rule_index,
10521            pred_index,
10522            &mut self.unknown_predicate_hits,
10523        )
10524    }
10525
10526    /// Builds the fail-loud error for unknown predicate coordinates recorded
10527    /// by the current parse, if any.
10528    fn unknown_semantic_error(&self) -> Option<AntlrError> {
10529        use std::fmt::Write as _;
10530        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
10531            return None;
10532        }
10533        let mut message = String::new();
10534        for (rule_index, pred_index) in &self.unknown_predicate_hits {
10535            if !message.is_empty() {
10536                message.push_str("; ");
10537            }
10538            let _ = match self.rule_names().get(*rule_index) {
10539                Some(rule_name) => write!(
10540                    message,
10541                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
10542                ),
10543                None => write!(
10544                    message,
10545                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
10546                ),
10547            };
10548        }
10549        for (rule_index, source_state) in &self.unhandled_action_hits {
10550            if !message.is_empty() {
10551                message.push_str("; ");
10552            }
10553            let _ = match self.rule_names().get(*rule_index) {
10554                Some(rule_name) => write!(
10555                    message,
10556                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
10557                ),
10558                None => write!(
10559                    message,
10560                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
10561                ),
10562            };
10563        }
10564        Some(AntlrError::Unsupported(message))
10565    }
10566
10567    /// Evaluates one lowered predicate expression at the requested input
10568    /// position.
10569    ///
10570    /// This sits in the prediction hot loop, so the context borrows the
10571    /// speculative member state read-only and the rule name by reference —
10572    /// no per-evaluation allocation. Only the hook escape path materializes
10573    /// owned copies, and only when a hook is actually consulted.
10574    fn parser_semir_predicate_matches(
10575        &mut self,
10576        semantics: &ParserSemantics,
10577        predicate: &ParserSemanticPredicate,
10578        request: ParserSemanticHookRequest<'_>,
10579    ) -> bool {
10580        self.input.seek(request.index);
10581        let rule_name = self
10582            .data
10583            .rule_names()
10584            .get(request.rule_index)
10585            .map(String::as_str);
10586        let unknown_predicate_policy = self.unknown_predicate_policy;
10587        let mut ctx = ParserSemIrCtx {
10588            input: &mut self.input,
10589            tree_storage: &self.tree,
10590            semantic_hooks: &mut self.semantic_hooks,
10591            rule_index: request.rule_index,
10592            coordinate_index: request.pred_index,
10593            rule_name,
10594            context: request.context,
10595            local_int_arg: request.local_int_arg,
10596            member_values: request.member_values,
10597            invoked_predicates: &mut self.invoked_predicates,
10598            unknown_predicate_policy,
10599            unknown_predicate_hits: &mut self.unknown_predicate_hits,
10600        };
10601        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
10602    }
10603
10604    fn fast_parser_predicate_matches(
10605        &mut self,
10606        context: Option<FastPredicateContext<'_>>,
10607        transition: ParserTransition<'_>,
10608        index: usize,
10609    ) -> bool {
10610        let Some(context) = context else {
10611            return true;
10612        };
10613        let rule_index = transition.arg0() as usize;
10614        let pred_index = transition.arg1() as usize;
10615        let key = (index, rule_index, pred_index);
10616        if let Some(result) = self.fast_predicate_cache.get(&key) {
10617            return *result;
10618        }
10619        let result = self.parser_predicate_matches(PredicateEval {
10620            index,
10621            rule_index,
10622            pred_index,
10623            predicates: context.predicates,
10624            semantics: context.semantics,
10625            context: None,
10626            local_int_arg: None,
10627            member_values: context.member_values,
10628        });
10629        self.fast_predicate_cache.insert(key, result);
10630        result
10631    }
10632
10633    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
10634        let PredicateEval {
10635            index,
10636            rule_index,
10637            pred_index,
10638            predicates,
10639            semantics,
10640            context,
10641            local_int_arg,
10642            member_values,
10643        } = eval;
10644        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
10645            semantics
10646                .predicates
10647                .iter()
10648                .find(|predicate| {
10649                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
10650                })
10651                .map(|predicate| (semantics, predicate))
10652        }) {
10653            return self.parser_semir_predicate_matches(
10654                semantics,
10655                predicate,
10656                ParserSemanticHookRequest {
10657                    index,
10658                    rule_index,
10659                    pred_index,
10660                    context,
10661                    local_int_arg,
10662                    member_values,
10663                },
10664            );
10665        }
10666        let Some((_, _, predicate)) = predicates
10667            .iter()
10668            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
10669        else {
10670            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
10671                index,
10672                rule_index,
10673                pred_index,
10674                context,
10675                local_int_arg,
10676                member_values,
10677            }) {
10678                return result;
10679            }
10680            return self.unknown_predicate_result(rule_index, pred_index);
10681        };
10682        self.input.seek(index);
10683        match predicate {
10684            ParserPredicate::True => true,
10685            ParserPredicate::False => false,
10686            ParserPredicate::FalseWithMessage { .. } => false,
10687            ParserPredicate::Invoke { value } => {
10688                let key = (rule_index, pred_index);
10689                if !self.invoked_predicates.contains(&key) {
10690                    self.invoked_predicates.push(key);
10691                    use std::io::Write as _;
10692                    let mut stdout = std::io::stdout().lock();
10693                    let _ = writeln!(stdout, "eval={value}");
10694                }
10695                *value
10696            }
10697            ParserPredicate::LookaheadTextEquals { offset, text } => self
10698                .input
10699                .lt(*offset)
10700                .is_some_and(|token| Token::text(&token) == Some(*text)),
10701            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
10702                self.la(*offset) != *token_type
10703            }
10704            ParserPredicate::TokenPairAdjacent => {
10705                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
10706                    return false;
10707                };
10708                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
10709                    return false;
10710                };
10711                first + 1 == second
10712            }
10713            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
10714                .and_then(|context| {
10715                    context
10716                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
10717                        .next()
10718                        .map(crate::tree::RuleNodeView::text)
10719                })
10720                .is_none_or(|actual| actual != *text),
10721            ParserPredicate::LocalIntEquals { value } => {
10722                local_int_arg.is_none_or(|(_, actual)| actual == *value)
10723            }
10724            ParserPredicate::LocalIntLessOrEqual { value } => {
10725                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
10726            }
10727            ParserPredicate::MemberModuloEquals {
10728                member,
10729                modulus,
10730                value,
10731                equals,
10732            } => {
10733                if *modulus == 0 {
10734                    return false;
10735                }
10736                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
10737                (actual == *value) == *equals
10738            }
10739            ParserPredicate::MemberEquals {
10740                member,
10741                value,
10742                equals,
10743            } => {
10744                let actual = member_values.get(member).copied().unwrap_or_default();
10745                (actual == *value) == *equals
10746            }
10747        }
10748    }
10749
10750    /// Returns a generated fail-option message for a predicate coordinate.
10751    fn parser_predicate_failure_message(
10752        &self,
10753        rule_index: usize,
10754        pred_index: usize,
10755        predicates: &[(usize, usize, ParserPredicate)],
10756    ) -> Option<&'static str> {
10757        predicates
10758            .iter()
10759            .find_map(|(rule, pred, predicate)| match predicate {
10760                ParserPredicate::FalseWithMessage { message }
10761                    if *rule == rule_index && *pred == pred_index =>
10762                {
10763                    Some(*message)
10764                }
10765                _ => None,
10766            })
10767    }
10768
10769    /// Returns a generated fail-option message for a `SemIR` predicate
10770    /// coordinate.
10771    pub fn parser_semantic_ir_predicate_failure_message(
10772        &self,
10773        rule_index: usize,
10774        pred_index: usize,
10775        semantics: &ParserSemantics,
10776    ) -> Option<&'static str> {
10777        semantics
10778            .predicates
10779            .iter()
10780            .find(|predicate| {
10781                predicate.rule_index == rule_index && predicate.pred_index == pred_index
10782            })
10783            .and_then(|predicate| predicate.failure_message)
10784    }
10785
10786    /// Returns the token-stream index after consuming `symbol` at `index`.
10787    ///
10788    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
10789    /// index stable and rely on `consumed_eof` to record that EOF was matched.
10790    /// The parser's stream cursor is left untouched: speculative recognition
10791    /// reads ahead by absolute index, so paying for `seek` on every visited
10792    /// state would dominate the hot path. Real consumption is committed by
10793    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
10794    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
10795        if symbol == TOKEN_EOF {
10796            return index;
10797        }
10798        self.input.next_visible_after(index)
10799    }
10800
10801    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
10802    /// decision that failed after consuming a shared prefix.
10803    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
10804        let text = display_input_text(&self.input.text(start_index, error_index));
10805        diagnostic_for_token(
10806            self.token_at(error_index).as_ref(),
10807            format!("no viable alternative at input '{text}'"),
10808        )
10809    }
10810
10811    /// Selects the diagnostic for a failed consuming transition after all
10812    /// recovery repairs have been ruled out.
10813    fn recovery_failure_diagnostic(
10814        &self,
10815        index: usize,
10816        decision_start_index: Option<usize>,
10817        expected_symbols: &BTreeSet<i32>,
10818    ) -> ParserDiagnostic {
10819        if expected_symbols.len() > 1 {
10820            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
10821                return self.no_viable_alternative(decision_start, index);
10822            }
10823        }
10824        diagnostic_for_token(
10825            self.token_at(index).as_ref(),
10826            format!(
10827                "mismatched input {} expecting {}",
10828                self.token_at(index)
10829                    .as_ref()
10830                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10831                self.expected_symbols_display(expected_symbols)
10832            ),
10833        )
10834    }
10835
10836    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
10837    /// instead of inserting missing tokens in the caller.
10838    fn eof_rule_recovery_diagnostic(
10839        &self,
10840        index: usize,
10841        expected_symbols: &BTreeSet<i32>,
10842        expected: &ExpectedTokens,
10843    ) -> ParserDiagnostic {
10844        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
10845            &expected.symbols
10846        } else {
10847            expected_symbols
10848        };
10849        diagnostic_for_token(
10850            self.token_at(index).as_ref(),
10851            format!(
10852                "mismatched input {} expecting {}",
10853                self.token_at(index)
10854                    .as_ref()
10855                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
10856                self.expected_symbols_display(symbols)
10857            ),
10858        )
10859    }
10860
10861    /// Returns token text for a buffered token interval used by generated
10862    /// `$text` actions.
10863    ///
10864    /// ANTLR treats EOF as a range boundary rather than printable input text,
10865    /// even when an action interval explicitly stops at the EOF token.
10866    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
10867        let Some(stop) = stop else {
10868            return String::new();
10869        };
10870        let stop = if self
10871            .token_at(stop)
10872            .is_some_and(|token| token.token_type() == TOKEN_EOF)
10873        {
10874            let Some(previous) = self.previous_token_index(stop) else {
10875                return String::new();
10876            };
10877            previous
10878        } else {
10879            stop
10880        };
10881        self.input.text(start, stop)
10882    }
10883
10884    /// Resets per-parse prediction diagnostics while keeping the parser-level
10885    /// reporting flag configured by generated harness code.
10886    fn clear_prediction_diagnostics(&mut self) {
10887        self.prediction_diagnostics.clear();
10888        self.reported_prediction_diagnostics.clear();
10889    }
10890
10891    /// Drops every per-parse cache that depends on ATN identity or pins
10892    /// recovery-symbol allocations.
10893    ///
10894    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
10895    /// same parser instance can legally be driven against different grammars
10896    /// in sequence. The four caches reset here are keyed by raw ATN
10897    /// coordinates (state numbers, rule indexes) and would silently hand back
10898    /// entries from a previous ATN if reused — pruning lookahead against the
10899    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
10900    /// for the rest of the process. Clearing them on every parse entry keeps
10901    /// the perf wins (caches still amortize within one parse) without making
10902    /// long-lived parsers leak memory or surface stale ATN data:
10903    ///
10904    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
10905    ///   functions of the ATN's state graph.
10906    /// * `state_expected_cache`, `state_expected_token_cache`,
10907    ///   `rule_stop_reach_cache`, and
10908    ///   `recovery_symbols_intern` together form
10909    ///   the identity invariant that lets `FastRecognizeKey` hash
10910    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
10911    ///   so a stale interned `Rc` cannot outlive its map entry.
10912    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
10913    ///   it is retained here and invalidated lazily when the ATN changes.
10914    fn reset_per_parse_caches(&mut self) {
10915        self.rule_first_set_cache.clear();
10916        self.decision_lookahead_cache.clear();
10917        self.ll1_decision_cache.clear();
10918        self.fast_predicate_cache.clear();
10919        self.rule_stop_reach_cache.clear();
10920        self.clean_memo_mode = CleanMemoMode::Probe;
10921        self.clean_memo_probe_seen.clear();
10922        self.clean_memo_probe_samples = 0;
10923        self.clean_memo_probe_repeats = 0;
10924        self.clean_memo_sparse_samples = 0;
10925        self.recovery_symbols_intern.clear();
10926        self.state_expected_cache.clear();
10927        self.state_expected_token_cache.clear();
10928    }
10929
10930    /// Buffers ANTLR-style diagnostic-listener messages for decision states
10931    /// where multiple clean alternatives survive full-context recognition.
10932    fn record_prediction_diagnostics(
10933        &mut self,
10934        atn: &Atn,
10935        state: AtnState<'_>,
10936        start_index: usize,
10937        outcomes: &[RecognizeOutcome],
10938    ) {
10939        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
10940            return;
10941        }
10942        let Some(decision) = atn
10943            .decision_to_state()
10944            .iter()
10945            .position(|state_number| state_number == state.state_number())
10946        else {
10947            return;
10948        };
10949        let Some(rule_index) = state.rule_index() else {
10950            return;
10951        };
10952        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
10953        for outcome in outcomes
10954            .iter()
10955            .filter(|outcome| outcome.diagnostics.is_empty())
10956        {
10957            let Some(alt) = outcome.decisions.first() else {
10958                continue;
10959            };
10960            alts_by_end
10961                .entry(outcome.index)
10962                .or_default()
10963                .insert(alt + 1);
10964        }
10965        let Some((&end_index, ambig_alts)) = alts_by_end
10966            .iter()
10967            .filter(|(_, alts)| alts.len() > 1)
10968            .max_by_key(|(end, _)| *end)
10969        else {
10970            return;
10971        };
10972        let rule_name = self
10973            .rule_names()
10974            .get(rule_index)
10975            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
10976        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
10977        let input = display_input_text(&self.input.text(start_index, stop_index));
10978        let alts = ambig_alts
10979            .iter()
10980            .map(usize::to_string)
10981            .collect::<Vec<_>>()
10982            .join(", ");
10983        let key = (decision, start_index, format!("{alts}:{input}"));
10984        if !self.reported_prediction_diagnostics.insert(key) {
10985            return;
10986        }
10987        let start_diagnostic = diagnostic_for_token(
10988            self.token_at(start_index),
10989            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
10990        );
10991        let stop_diagnostic = diagnostic_for_token(
10992            self.token_at(stop_index),
10993            format!(
10994                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
10995            ),
10996        );
10997        self.prediction_diagnostics.push(start_diagnostic);
10998        self.prediction_diagnostics.push(stop_diagnostic);
10999    }
11000
11001    /// Formats the tokens expected from an ATN state using ANTLR display names.
11002    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11003        expected_symbols_display(
11004            &state_expected_symbols(atn, state_number),
11005            self.vocabulary(),
11006        )
11007    }
11008
11009    /// Expected-token set at the parser's current ATN state — ANTLR's
11010    /// `getExpectedTokens()`. Generated recognizers expose this as
11011    /// `self.expected_tokens()` for embedded test actions
11012    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
11013    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
11014        let state = usize::try_from(self.data().state()).unwrap_or(0);
11015        ExpectedTokenSet {
11016            symbols: state_expected_symbols(atn, state),
11017        }
11018    }
11019
11020    /// Enables the bail error strategy: the first syntax error aborts the
11021    /// parse instead of recovering.
11022    pub const fn set_bail_on_error(&mut self, bail: bool) {
11023        self.bail_on_error = bail;
11024    }
11025
11026    /// Whether the bail error strategy is active.
11027    #[must_use]
11028    pub const fn bail_on_error(&self) -> bool {
11029        self.bail_on_error
11030    }
11031
11032    /// Names of the rules on the live invocation stack, current rule first —
11033    /// ANTLR's `getRuleInvocationStack()`.
11034    pub fn rule_invocation_stack(&self) -> Vec<String> {
11035        self.rule_context_stack
11036            .iter()
11037            .rev()
11038            .map(|frame| {
11039                self.data()
11040                    .rule_names()
11041                    .get(frame.rule_index)
11042                    .cloned()
11043                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
11044            })
11045            .collect()
11046    }
11047
11048    /// Invoking-state chain for the active rule context, current rule first.
11049    ///
11050    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
11051    pub fn active_invocation_states(&self) -> Vec<isize> {
11052        self.rule_context_stack
11053            .iter()
11054            .skip(1)
11055            .rev()
11056            .map(|frame| frame.invoking_state)
11057            .collect()
11058    }
11059
11060    /// Formats a buffered token in ANTLR's diagnostic token display form.
11061    pub fn token_display_at(&self, index: usize) -> Option<String> {
11062        self.token_at(index).map(|token| format!("{token}"))
11063    }
11064}
11065
11066impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
11067where
11068    S: TokenSource,
11069    H: SemanticHooks,
11070{
11071    fn parse_rule(
11072        &mut self,
11073        rule_index: usize,
11074        invoking_state: isize,
11075        precedence: i32,
11076    ) -> DirectAdaptiveParseResult<ParseTree> {
11077        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
11078            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
11079        )?;
11080        let stop_state = self
11081            .atn
11082            .rule_to_stop_state()
11083            .get(rule_index)
11084            .filter(|state| *state != usize::MAX)
11085            .ok_or(DirectAdaptiveParseControl::Fallback(
11086                DirectAdaptiveFallback::MissingAtn,
11087            ))?;
11088        let start_index = self.parser.current_visible_index();
11089        let mut context = ParserRuleContext::new(rule_index, invoking_state);
11090        if let Some(token) = self.parser.token_id_at(start_index) {
11091            self.parser.set_context_start(&mut context, token);
11092        }
11093        let mut state_number = start_state;
11094        let mut consumed_eof = false;
11095        while state_number != stop_state {
11096            self.step()?;
11097            let (transition, boundary) = self.next_transition(state_number, precedence)?;
11098            if boundary.is_some() {
11099                return Err(DirectAdaptiveParseControl::Fallback(
11100                    DirectAdaptiveFallback::LeftRecursiveBoundary,
11101                ));
11102            }
11103            match transition.data() {
11104                Transition::Epsilon { target } => {
11105                    state_number = target;
11106                }
11107                Transition::Precedence {
11108                    target,
11109                    precedence: transition_precedence,
11110                } => {
11111                    if transition_precedence < precedence {
11112                        return Err(DirectAdaptiveParseControl::Fallback(
11113                            DirectAdaptiveFallback::Precedence,
11114                        ));
11115                    }
11116                    state_number = target;
11117                }
11118                Transition::Rule {
11119                    rule_index,
11120                    follow_state,
11121                    precedence: rule_precedence,
11122                    ..
11123                } => {
11124                    let child = self.parse_rule(
11125                        rule_index,
11126                        invoking_state_number(state_number),
11127                        rule_precedence,
11128                    )?;
11129                    if self.parser.build_parse_trees {
11130                        self.parser.tree.add_child(&mut context, child);
11131                    }
11132                    state_number = follow_state;
11133                }
11134                Transition::Atom { .. }
11135                | Transition::Range { .. }
11136                | Transition::Set { .. }
11137                | Transition::NotSet { .. }
11138                | Transition::Wildcard { .. } => {
11139                    let (matched_eof, child) = self.consume_transition(transition)?;
11140                    consumed_eof |= matched_eof;
11141                    if let Some(child) = child {
11142                        self.parser.tree.add_child(&mut context, child);
11143                    }
11144                    state_number = transition.target();
11145                }
11146                Transition::Predicate { .. } => {
11147                    return Err(DirectAdaptiveParseControl::Fallback(
11148                        DirectAdaptiveFallback::Predicate,
11149                    ));
11150                }
11151                Transition::Action { .. } => {
11152                    return Err(DirectAdaptiveParseControl::Fallback(
11153                        DirectAdaptiveFallback::Action,
11154                    ));
11155                }
11156            }
11157        }
11158
11159        let stop_index = self
11160            .parser
11161            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
11162        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
11163            self.parser.set_context_stop(&mut context, token);
11164        }
11165        Ok(self.parser.rule_node(context))
11166    }
11167
11168    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
11169        self.steps += 1;
11170        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
11171            return Err(DirectAdaptiveParseControl::Fallback(
11172                DirectAdaptiveFallback::StepLimit,
11173            ));
11174        }
11175        Ok(())
11176    }
11177
11178    fn next_transition(
11179        &mut self,
11180        state_number: usize,
11181        precedence: i32,
11182    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
11183        let state = self
11184            .atn
11185            .state(state_number)
11186            .ok_or(DirectAdaptiveParseControl::Fallback(
11187                DirectAdaptiveFallback::MissingAtn,
11188            ))?;
11189        if state.is_rule_stop() {
11190            return Err(DirectAdaptiveParseControl::Fallback(
11191                DirectAdaptiveFallback::RuleStop,
11192            ));
11193        }
11194        let transition_index =
11195            self.transition_index(state_number, state.transitions().len(), precedence)?;
11196        let transition = state.transitions().get(transition_index).ok_or(
11197            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
11198        )?;
11199        let boundary = match &transition.data() {
11200            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
11201                left_recursive_boundary(self.atn, state, *target)
11202            }
11203            _ => None,
11204        };
11205        Ok((transition, boundary))
11206    }
11207
11208    fn transition_index(
11209        &mut self,
11210        state_number: usize,
11211        transition_count: usize,
11212        precedence: i32,
11213    ) -> DirectAdaptiveParseResult<usize> {
11214        match transition_count {
11215            0 => Err(DirectAdaptiveParseControl::Fallback(
11216                DirectAdaptiveFallback::NoTransition,
11217            )),
11218            1 => Ok(0),
11219            _ => {
11220                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
11221                    return Ok(alt);
11222                }
11223                let decision = self
11224                    .decision_by_state
11225                    .get(state_number)
11226                    .and_then(|decision| *decision)
11227                    .ok_or(DirectAdaptiveParseControl::Fallback(
11228                        DirectAdaptiveFallback::UnknownDecision,
11229                    ))?;
11230                let prediction = self
11231                    .simulator
11232                    .adaptive_predict_stream_info_with_precedence(
11233                        decision,
11234                        direct_precedence(precedence),
11235                        &mut self.parser.input,
11236                    )
11237                    .map_err(|_| {
11238                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
11239                    })?;
11240                if prediction.has_semantic_context {
11241                    return Err(DirectAdaptiveParseControl::Fallback(
11242                        DirectAdaptiveFallback::SemanticContext,
11243                    ));
11244                }
11245                prediction
11246                    .alt
11247                    .checked_sub(1)
11248                    .filter(|index| *index < transition_count)
11249                    .ok_or(DirectAdaptiveParseControl::Fallback(
11250                        DirectAdaptiveFallback::InvalidAlt,
11251                    ))
11252            }
11253        }
11254    }
11255
11256    fn ll1_transition_index(
11257        &mut self,
11258        state_number: usize,
11259        transition_count: usize,
11260    ) -> DirectAdaptiveParseResult<Option<usize>> {
11261        let state = self
11262            .atn
11263            .state(state_number)
11264            .ok_or(DirectAdaptiveParseControl::Fallback(
11265                DirectAdaptiveFallback::MissingAtn,
11266            ))?;
11267        if state.precedence_rule_decision() {
11268            return Ok(None);
11269        }
11270        let Some(rule_stop) = state
11271            .rule_index()
11272            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
11273        else {
11274            return Ok(None);
11275        };
11276        let symbol = self.parser.input.la_token(1);
11277        let entry = self
11278            .parser
11279            .cached_decision_lookahead(self.atn, state, rule_stop);
11280        Ok(
11281            ll1_greedy_alt(&entry, symbol, state.non_greedy())
11282                .filter(|alt| *alt < transition_count),
11283        )
11284    }
11285
11286    fn consume_transition(
11287        &mut self,
11288        transition: ParserTransition<'_>,
11289    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
11290        let symbol = self.parser.input.la_token(1);
11291        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
11292            return Err(DirectAdaptiveParseControl::Fallback(
11293                DirectAdaptiveFallback::TokenMismatch,
11294            ));
11295        }
11296        let token = self
11297            .parser
11298            .input
11299            .lt_id(1)
11300            .ok_or(DirectAdaptiveParseControl::Fallback(
11301                DirectAdaptiveFallback::TokenMismatch,
11302            ))?;
11303        let matched_eof = symbol == TOKEN_EOF;
11304        if !matched_eof {
11305            self.parser.consume();
11306        }
11307        let child = self
11308            .parser
11309            .build_parse_trees
11310            .then(|| self.parser.terminal_tree(token));
11311        Ok((matched_eof, child))
11312    }
11313}
11314
11315/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
11316/// transformed left-recursive rule.
11317fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
11318    if !state.precedence_rule_decision() {
11319        return None;
11320    }
11321    let target_state = atn.state(target)?;
11322    if target_state.kind() == AtnStateKind::LoopEnd {
11323        return None;
11324    }
11325    state.rule_index()
11326}
11327
11328/// Selects the first outer alternative observed for a rule path.
11329///
11330/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
11331/// outer decision. The metadata recognizer only needs this when a generated
11332/// grammar opts into that target template; otherwise the value remains `0` and
11333/// parse-tree rendering is unchanged.
11334fn next_alt_number(
11335    state: AtnState<'_>,
11336    transition_count: usize,
11337    transition_index: usize,
11338    current_alt_number: usize,
11339    track_alt_numbers: bool,
11340) -> usize {
11341    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
11342        return current_alt_number;
11343    }
11344    if matches!(
11345        state.kind(),
11346        AtnStateKind::Basic
11347            | AtnStateKind::BlockStart
11348            | AtnStateKind::PlusBlockStart
11349            | AtnStateKind::StarBlockStart
11350            | AtnStateKind::StarLoopEntry
11351    ) && !state.precedence_rule_decision()
11352    {
11353        return transition_index + 1;
11354    }
11355    current_alt_number
11356}
11357
11358/// Converts an ATN state number into the signed invoking-state slot used by
11359/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
11360fn invoking_state_number(state_number: usize) -> isize {
11361    isize::try_from(state_number).unwrap_or(isize::MAX)
11362}
11363
11364const fn packed_i32(value: u32) -> i32 {
11365    i32::from_le_bytes(value.to_le_bytes())
11366}
11367
11368fn direct_precedence(precedence: i32) -> usize {
11369    usize::try_from(precedence.max(0)).unwrap_or_default()
11370}
11371
11372fn token_input_display(token: &impl Token) -> String {
11373    format!("'{}'", token.text().unwrap_or("<EOF>"))
11374}
11375
11376fn display_input_text(text: &str) -> String {
11377    let mut out = String::new();
11378    for ch in text.chars() {
11379        match ch {
11380            '\n' => out.push_str("\\n"),
11381            '\r' => out.push_str("\\r"),
11382            '\t' => out.push_str("\\t"),
11383            other => out.push(other),
11384        }
11385    }
11386    out
11387}
11388
11389fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
11390    let (line, column) = token.map_or((0, 0), |token| (token.line(), token.column()));
11391    ParserDiagnostic {
11392        line,
11393        column,
11394        message,
11395    }
11396}
11397
11398fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
11399    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
11400}
11401
11402fn expected_symbols_display_iter(
11403    symbols: impl IntoIterator<Item = i32>,
11404    vocabulary: &Vocabulary,
11405) -> String {
11406    let items = symbols
11407        .into_iter()
11408        .map(|symbol| expected_symbol_display(symbol, vocabulary))
11409        .collect::<Vec<_>>();
11410    if let [single] = items.as_slice() {
11411        return single.clone();
11412    }
11413    format!("{{{}}}", items.join(", "))
11414}
11415
11416fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
11417    if symbol == TOKEN_EOF {
11418        return "<EOF>".to_owned();
11419    }
11420    vocabulary.display_name(symbol)
11421}
11422
11423fn caller_follow_token_info_for_stream<S: TokenSource>(
11424    input: &mut CommonTokenStream<S>,
11425    index: usize,
11426) -> (i32, bool, bool) {
11427    // Generated callers own statement separators; leave them available when
11428    // an interpreted child rule can either stop before or consume one.
11429    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
11430        input.fill();
11431    }
11432    let token_type = input.token_type_at_index(index);
11433    let visible_channel = input.channel();
11434    let token = input.get(index);
11435    let is_boundary = token
11436        .as_ref()
11437        .and_then(Token::text)
11438        .is_some_and(is_caller_follow_boundary_text);
11439    let is_boundary_gap = token.as_ref().is_some_and(|token| {
11440        token.channel() != visible_channel || is_caller_follow_boundary_gap_text(token.text())
11441    });
11442    (token_type, is_boundary, is_boundary_gap)
11443}
11444
11445fn is_caller_follow_boundary_text(text: &str) -> bool {
11446    text.chars().any(|ch| ch == ';' || ch == '\n')
11447        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11448}
11449
11450fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
11451    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
11452}
11453
11454/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
11455/// Inline insertion in those precedence loops can synthesize a missing operand
11456/// before an operator and then block the legitimate loop-exit path.
11457fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
11458    let Some(rule_index) = state.rule_index() else {
11459        return false;
11460    };
11461    atn.rule_to_start_state()
11462        .get(rule_index)
11463        .and_then(|state_number| atn.state(state_number))
11464        .is_some_and(AtnState::left_recursive_rule)
11465}
11466
11467/// Picks the better of two `parse_atn_rule` passes (with and without the
11468/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
11469/// recovered one; among recovered outcomes the second pass is preferred
11470/// because the no-prefilter walk reaches ANTLR-style recovery inside child
11471/// rules. If both passes failed, the second pass's expected-token snapshot
11472/// is returned so the caller renders the same diagnostic ANTLR would.
11473fn select_better_top_outcome(
11474    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11475    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
11476    arena: &RecognitionArena,
11477) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
11478    match (first, second) {
11479        (Ok(first), Ok(second)) => {
11480            if arena.diagnostics(first.0.diagnostics).next().is_none() {
11481                Ok(first)
11482            } else {
11483                Ok(second)
11484            }
11485        }
11486        (Ok(first), Err(_)) => Ok(first),
11487        (Err(_), Ok(second)) => Ok(second),
11488        (Err(_), Err(second_expected)) => Err(second_expected),
11489    }
11490}
11491
11492/// Chooses the outermost parse result that consumed the most input.
11493///
11494/// The recognizer intentionally keeps shorter endpoints available while walking
11495/// nested rule transitions so callers can satisfy following tokens such as
11496/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
11497fn select_best_fast_outcome(
11498    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
11499    prediction_mode: PredictionMode,
11500    caller_follow: Option<&TokenBitSet>,
11501    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
11502    arena: &RecognitionArena,
11503) -> Option<FastRecognizeOutcome> {
11504    let mut best = None;
11505    let mut best_caller_follow = None;
11506    for outcome in outcomes {
11507        if matches!(
11508            prediction_mode,
11509            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11510        ) && outcome.diagnostics.is_empty()
11511            && let Some(follow) = caller_follow
11512        {
11513            let (token_type, is_boundary, _) = token_info_at(outcome.index);
11514            if is_boundary && follow.contains(token_type) {
11515                let replace =
11516                    best_caller_follow
11517                        .as_ref()
11518                        .is_none_or(|existing: &FastRecognizeOutcome| {
11519                            (outcome.index, outcome.consumed_eof)
11520                                < (existing.index, existing.consumed_eof)
11521                        });
11522                if replace {
11523                    best_caller_follow = Some(outcome);
11524                }
11525            }
11526        }
11527        let Some(existing) = best else {
11528            best = Some(outcome);
11529            continue;
11530        };
11531        let outcome_position = (outcome.index, outcome.consumed_eof);
11532        let best_position = (existing.index, existing.consumed_eof);
11533        let better = match prediction_mode {
11534            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
11535                outcome_position,
11536                outcome.diagnostics,
11537                best_position,
11538                existing.diagnostics,
11539                arena,
11540            ),
11541            PredictionMode::Sll => outcome.index > existing.index,
11542        };
11543        best = Some(if better { outcome } else { existing });
11544    }
11545    let should_use_caller_follow =
11546        best_caller_follow
11547            .as_ref()
11548            .zip(best.as_ref())
11549            .is_some_and(|(candidate, selected)| {
11550                if !selected.diagnostics.is_empty() {
11551                    return true;
11552                }
11553                candidate.index < selected.index
11554                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
11555            });
11556    if should_use_caller_follow {
11557        best_caller_follow
11558    } else {
11559        best
11560    }
11561}
11562
11563fn select_best_outcome(
11564    outcomes: impl Iterator<Item = RecognizeOutcome>,
11565    prediction_mode: PredictionMode,
11566    arena: &RecognitionArena,
11567) -> Option<RecognizeOutcome> {
11568    let outcomes = outcomes.collect::<Vec<_>>();
11569    let prefer_first_tie = outcomes
11570        .iter()
11571        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
11572    outcomes.into_iter().reduce(|best, outcome| {
11573        let outcome_position = (outcome.index, outcome.consumed_eof);
11574        let best_position = (best.index, best.consumed_eof);
11575        let better = match prediction_mode {
11576            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
11577                outcome_is_better(
11578                    outcome_position,
11579                    outcome.diagnostics,
11580                    best_position,
11581                    best.diagnostics,
11582                    arena,
11583                ) || (!prefer_first_tie
11584                    && outcome_position == best_position
11585                    && arena.diagnostics_len(outcome.diagnostics)
11586                        == arena.diagnostics_len(best.diagnostics)
11587                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
11588                        == arena.diagnostics_recovery_rank(best.diagnostics)
11589                    && (outcome.decisions < best.decisions
11590                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
11591            }
11592            PredictionMode::Sll => {
11593                outcome_position > best_position
11594                    || (outcome_position == best_position
11595                        && !prefer_first_tie
11596                        && (outcome.decisions < best.decisions
11597                            || (outcome.decisions == best.decisions
11598                                && outcome_is_better(
11599                                    outcome_position,
11600                                    outcome.diagnostics,
11601                                    best_position,
11602                                    best.diagnostics,
11603                                    arena,
11604                                ))))
11605            }
11606        };
11607        if better {
11608            return outcome;
11609        }
11610        best
11611    })
11612}
11613
11614/// Records the serialized transition order at parser decision states.
11615///
11616/// When two clean paths consume the same input, ANTLR's adaptive prediction
11617/// chooses by alternative order. Keeping this compact trace lets the metadata
11618/// recognizer distinguish greedy and non-greedy optional blocks without a full
11619/// prediction simulator.
11620fn transition_decision(
11621    atn: &Atn,
11622    state: AtnState<'_>,
11623    transition_count: usize,
11624    transition_index: usize,
11625    predicates: &[(usize, usize, ParserPredicate)],
11626) -> Option<usize> {
11627    if transition_count <= 1
11628        || state.precedence_rule_decision()
11629        || decision_reaches_unsupported_predicate(atn, state, predicates)
11630    {
11631        return None;
11632    }
11633    Some(transition_index)
11634}
11635
11636/// Reports whether a state should reset the active no-viable decision start.
11637///
11638/// Loop entry/back states are continuations of the surrounding adaptive
11639/// prediction; resetting at those states would turn LL-star failures back into
11640/// ordinary mismatches.
11641fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
11642    transition_count > 1
11643        && !matches!(
11644            state.kind(),
11645            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
11646        )
11647}
11648
11649/// Marks a farthest expected-token set as no-viable when multiple alternatives
11650/// failed after the active decision had already consumed input.
11651fn record_no_viable_if_ambiguous(
11652    expected: &mut ExpectedTokens,
11653    decision_start_index: Option<usize>,
11654    index: usize,
11655) {
11656    if expected.index == Some(index) && expected.symbols.len() > 1 {
11657        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11658            expected.record_no_viable(decision_start, index);
11659        }
11660    }
11661}
11662
11663/// Records a no-viable decision caused by a failed semantic predicate before
11664/// any consuming transition can contribute an expected-token set.
11665const fn record_predicate_no_viable(
11666    expected: &mut ExpectedTokens,
11667    decision_start_index: Option<usize>,
11668    index: usize,
11669) {
11670    if let Some(decision_start) = decision_start_index {
11671        expected.record_no_viable(decision_start, index);
11672    }
11673}
11674
11675/// Returns the active decision start only when the error is past that start.
11676const fn no_viable_decision_start(
11677    decision_start_index: Option<usize>,
11678    index: usize,
11679) -> Option<usize> {
11680    match decision_start_index {
11681        Some(start) if index > start => Some(start),
11682        _ => None,
11683    }
11684}
11685
11686/// Restores expected-token bookkeeping when a child rule found a clean
11687/// consuming path; failures in longer child alternatives should not pollute the
11688/// caller's final expectation set.
11689fn restore_expected(
11690    children: &[RecognizeOutcome],
11691    child_start_index: usize,
11692    expected: &mut ExpectedTokens,
11693    snapshot: ExpectedTokens,
11694    preserve_child_expected: bool,
11695) {
11696    if preserve_child_expected {
11697        return;
11698    }
11699    if children
11700        .iter()
11701        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
11702    {
11703        *expected = snapshot;
11704    }
11705}
11706
11707/// Reports whether a decision can reach a predicate the generator did not
11708/// translate. Static alternative order is unsafe for those context predicates.
11709fn decision_reaches_unsupported_predicate(
11710    atn: &Atn,
11711    state: AtnState<'_>,
11712    predicates: &[(usize, usize, ParserPredicate)],
11713) -> bool {
11714    state.transitions().iter().any(|transition| {
11715        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
11716    })
11717}
11718
11719/// Walks epsilon-like edges from one transition to find unsupported predicates.
11720fn transition_reaches_unsupported_predicate(
11721    atn: &Atn,
11722    transition: ParserTransition<'_>,
11723    predicates: &[(usize, usize, ParserPredicate)],
11724    visited: &mut BTreeSet<usize>,
11725) -> bool {
11726    match &transition.data() {
11727        Transition::Predicate {
11728            rule_index,
11729            pred_index,
11730            ..
11731        } => !predicates
11732            .iter()
11733            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
11734        Transition::Epsilon { target }
11735        | Transition::Action { target, .. }
11736        | Transition::Rule { target, .. } => {
11737            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
11738        }
11739        Transition::Precedence { .. }
11740        | Transition::Atom { .. }
11741        | Transition::Range { .. }
11742        | Transition::Set { .. }
11743        | Transition::NotSet { .. }
11744        | Transition::Wildcard { .. } => false,
11745    }
11746}
11747
11748/// Finds an unsupported predicate reachable before a consuming transition.
11749fn state_reaches_unsupported_predicate(
11750    atn: &Atn,
11751    state_number: usize,
11752    predicates: &[(usize, usize, ParserPredicate)],
11753    visited: &mut BTreeSet<usize>,
11754) -> bool {
11755    if !visited.insert(state_number) {
11756        return false;
11757    }
11758    let Some(state) = atn.state(state_number) else {
11759        return false;
11760    };
11761    state.transitions().iter().any(|transition| {
11762        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
11763    })
11764}
11765
11766/// Adds a decision step to the front of an already-recognized suffix path.
11767fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
11768    if let Some(decision) = decision {
11769        outcome.decisions.insert(0, decision);
11770    }
11771}
11772
11773fn outcome_is_better(
11774    outcome_position: (usize, bool),
11775    outcome_diagnostics: DiagnosticSeqId,
11776    best_position: (usize, bool),
11777    best_diagnostics: DiagnosticSeqId,
11778    arena: &RecognitionArena,
11779) -> bool {
11780    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
11781    let best_len = arena.diagnostics_len(best_diagnostics);
11782    outcome_position > best_position
11783        || (outcome_position == best_position
11784            && (outcome_len < best_len
11785                || (outcome_len == best_len
11786                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
11787                        < arena.diagnostics_recovery_rank(best_diagnostics))))
11788}
11789
11790fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
11791    if outcomes
11792        .iter()
11793        .any(|outcome| outcome.diagnostics.is_empty())
11794    {
11795        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11796    }
11797}
11798
11799fn discard_recovered_outcomes_if_clean_path_exists(
11800    outcomes: &mut Vec<RecognizeOutcome>,
11801    arena: &RecognitionArena,
11802) {
11803    if outcomes
11804        .iter()
11805        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
11806    {
11807        return;
11808    }
11809    if outcomes
11810        .iter()
11811        .any(|outcome| outcome.diagnostics.is_empty())
11812    {
11813        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
11814    }
11815}
11816
11817/// Reports whether a recovered outcome came from an explicit predicate
11818/// fail-option and therefore should compete with shorter clean loop exits.
11819fn outcome_has_rule_failure_diagnostic(
11820    outcome: &RecognizeOutcome,
11821    arena: &RecognitionArena,
11822) -> bool {
11823    arena
11824        .diagnostics(outcome.diagnostics)
11825        .any(|diagnostic| diagnostic.message.starts_with("rule "))
11826}
11827
11828/// Removes equivalent endpoints before memoizing a state result while
11829/// preserving ATN transition-discovery order.
11830///
11831/// Outcomes are compared on observable recognition state — the input index,
11832/// EOF consumption, and diagnostics — without descending into the parse-tree
11833/// fragment carried by `nodes`. Two paths reaching the same point with
11834/// different node trees would otherwise prevent memoization from collapsing
11835/// equivalent suffixes and explode the speculative-path cache.
11836///
11837/// The first occurrence per recognition key wins, which matches ANTLR's
11838/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
11839/// transitions before loop-exit, so the first-discovered outcome carries the
11840/// greedy parse-tree fragment.
11841fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
11842    if outcomes.len() < 2 {
11843        return;
11844    }
11845    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
11846    outcomes.retain(|outcome| {
11847        seen.insert((
11848            outcome.index,
11849            outcome.consumed_eof,
11850            arena.diagnostics_len(outcome.diagnostics),
11851            arena.diagnostics_recovery_rank(outcome.diagnostics),
11852        ))
11853    });
11854}
11855
11856const FAST_OUTCOME_INLINE_KEYS: usize = 8;
11857const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
11858const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
11859const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
11860
11861#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11862enum FastOutcomeDedupStrategy {
11863    Inline,
11864    Dense,
11865    Sparse,
11866}
11867
11868impl FastOutcomeDedupScratch {
11869    fn prepare_dense(&mut self, word_count: usize) {
11870        while let Some(word_index) = self.touched_dense_words.pop() {
11871            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
11872        }
11873        if self.dense_words.len() < word_count {
11874            self.dense_words.resize(word_count, 0);
11875        }
11876    }
11877}
11878
11879fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
11880    let first_index = outcomes.first()?.index;
11881    let (min_index, max_index) = outcomes[1..].iter().fold(
11882        (first_index, first_index),
11883        |(min_index, max_index), outcome| {
11884            (min_index.min(outcome.index), max_index.max(outcome.index))
11885        },
11886    );
11887    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
11888    let bit_count = index_span.checked_mul(2)?;
11889    let word_count =
11890        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
11891    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
11892    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
11893    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
11894        .then_some((min_index, word_count))
11895}
11896
11897#[cfg(feature = "perf-counters")]
11898fn record_clean_fast_outcome_dedup(
11899    strategy: FastOutcomeDedupStrategy,
11900    input_len: usize,
11901    output_len: usize,
11902    dense_words: usize,
11903) {
11904    let counter = match strategy {
11905        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
11906        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
11907        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
11908    };
11909    perf_counters::inc(
11910        &perf_counters::OUTCOME_DEDUPE_INPUTS,
11911        u64::try_from(input_len).unwrap_or(u64::MAX),
11912    );
11913    perf_counters::inc(
11914        &perf_counters::OUTCOME_DEDUPE_REMOVED,
11915        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
11916    );
11917    perf_counters::inc(counter, 1);
11918    perf_counters::inc(
11919        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
11920        u64::try_from(dense_words).unwrap_or(u64::MAX),
11921    );
11922}
11923
11924/// Removes duplicate clean endpoints while preserving transition-discovery
11925/// order. Tiny lists stay on the stack; larger compact ranges use a direct
11926/// bitmap, and only wide sparse ranges pay for hashing.
11927fn dedupe_clean_fast_outcomes(
11928    outcomes: &mut Vec<FastRecognizeOutcome>,
11929    scratch: &mut FastOutcomeDedupScratch,
11930) -> FastOutcomeDedupStrategy {
11931    #[cfg(feature = "perf-counters")]
11932    let input_len = outcomes.len();
11933    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
11934        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
11935        let mut inline_len = 0_usize;
11936        outcomes.retain(|outcome| {
11937            let key = (outcome.index, outcome.consumed_eof);
11938            if inline_keys[..inline_len].contains(&key) {
11939                return false;
11940            }
11941            inline_keys[inline_len] = key;
11942            inline_len += 1;
11943            true
11944        });
11945        #[cfg(feature = "perf-counters")]
11946        record_clean_fast_outcome_dedup(
11947            FastOutcomeDedupStrategy::Inline,
11948            input_len,
11949            outcomes.len(),
11950            0,
11951        );
11952        return FastOutcomeDedupStrategy::Inline;
11953    }
11954
11955    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
11956        scratch.prepare_dense(word_count);
11957        outcomes.retain(|outcome| {
11958            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
11959            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
11960            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
11961            let word = &mut scratch.dense_words[word_index];
11962            if *word & bit != 0 {
11963                return false;
11964            }
11965            if *word == 0 {
11966                scratch
11967                    .touched_dense_words
11968                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
11969            }
11970            *word |= bit;
11971            true
11972        });
11973        #[cfg(feature = "perf-counters")]
11974        record_clean_fast_outcome_dedup(
11975            FastOutcomeDedupStrategy::Dense,
11976            input_len,
11977            outcomes.len(),
11978            word_count,
11979        );
11980        return FastOutcomeDedupStrategy::Dense;
11981    }
11982
11983    scratch.sparse_keys.clear();
11984    scratch.sparse_keys.reserve(outcomes.len());
11985    outcomes.retain(|outcome| {
11986        scratch
11987            .sparse_keys
11988            .insert((outcome.index, outcome.consumed_eof))
11989    });
11990    #[cfg(feature = "perf-counters")]
11991    record_clean_fast_outcome_dedup(
11992        FastOutcomeDedupStrategy::Sparse,
11993        input_len,
11994        outcomes.len(),
11995        0,
11996    );
11997    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
11998        scratch.sparse_keys = FxHashSet::default();
11999    }
12000    FastOutcomeDedupStrategy::Sparse
12001}
12002
12003/// Sorts and removes equivalent endpoints, including action traces and the
12004/// arena-backed node sequence's structural contents.
12005fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
12006    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
12007    outcomes
12008        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
12009}
12010
12011fn compare_recognize_outcomes(
12012    left: &RecognizeOutcome,
12013    right: &RecognizeOutcome,
12014    arena: &RecognitionArena,
12015) -> Ordering {
12016    left.index
12017        .cmp(&right.index)
12018        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
12019        .then_with(|| left.alt_number.cmp(&right.alt_number))
12020        .then_with(|| left.member_values.cmp(&right.member_values))
12021        .then_with(|| left.return_values.cmp(&right.return_values))
12022        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
12023        .then_with(|| left.decisions.cmp(&right.decisions))
12024        .then_with(|| left.actions.cmp(&right.actions))
12025        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
12026}
12027
12028impl<S, H> Recognizer for BaseParser<S, H>
12029where
12030    S: TokenSource,
12031    H: SemanticHooks,
12032{
12033    fn data(&self) -> &RecognizerData {
12034        &self.data
12035    }
12036
12037    fn data_mut(&mut self) -> &mut RecognizerData {
12038        &mut self.data
12039    }
12040}
12041
12042impl<S, H> Parser for BaseParser<S, H>
12043where
12044    S: TokenSource,
12045    H: SemanticHooks,
12046{
12047    fn build_parse_trees(&self) -> bool {
12048        self.build_parse_trees
12049    }
12050
12051    fn set_build_parse_trees(&mut self, build: bool) {
12052        self.build_parse_trees = build;
12053    }
12054
12055    fn number_of_syntax_errors(&self) -> usize {
12056        Self::number_of_syntax_errors(self)
12057    }
12058
12059    fn report_diagnostic_errors(&self) -> bool {
12060        self.report_diagnostic_errors
12061    }
12062
12063    fn set_report_diagnostic_errors(&mut self, report: bool) {
12064        self.report_diagnostic_errors = report;
12065    }
12066
12067    fn prediction_mode(&self) -> PredictionMode {
12068        self.prediction_mode
12069    }
12070
12071    fn set_prediction_mode(&mut self, mode: PredictionMode) {
12072        self.prediction_mode = mode;
12073    }
12074}
12075
12076#[cfg(test)]
12077mod tests {
12078    use super::*;
12079    use crate::atn::parser::{
12080        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
12081    };
12082    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
12083    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
12084    use crate::token_stream::CommonTokenStream;
12085    use crate::tree::{NodeKind, ParseTreeStats};
12086    use crate::vocabulary::Vocabulary;
12087    use std::cell::RefCell;
12088    use std::mem::size_of;
12089    use std::rc::Rc;
12090    use std::sync::{Arc, Mutex};
12091
12092    #[test]
12093    fn fx_hasher_write_matches_typed_methods_for_full_words() {
12094        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
12095        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
12096        // fields) must hash the same as the typed methods, otherwise an
12097        // `FxHashMap` keyed on such a type silently disagrees with itself
12098        // depending on which entry point the caller used. Verify the
12099        // little-endian word equivalence this PR established.
12100        let value: u64 = 0x0102_0304_0506_0708;
12101        let mut typed = FxHasher::default();
12102        typed.write_u64(value);
12103        let mut bytewise = FxHasher::default();
12104        bytewise.write(&value.to_le_bytes());
12105        assert_eq!(typed.finish(), bytewise.finish());
12106    }
12107
12108    #[derive(Clone, Debug)]
12109    struct TestToken {
12110        spec: TokenSpec,
12111        id: TokenId,
12112        source_name: String,
12113    }
12114
12115    impl TestToken {
12116        fn new(token_type: i32) -> Self {
12117            Self {
12118                spec: TokenSpec::explicit(token_type, ""),
12119                id: TokenId::try_from(0).expect("zero token ID"),
12120                source_name: String::new(),
12121            }
12122        }
12123
12124        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
12125            Self {
12126                spec: TokenSpec::eof(index, index, line, column),
12127                id: TokenId::try_from(0).expect("zero token ID"),
12128                source_name: source_name.to_owned(),
12129            }
12130        }
12131
12132        fn with_text(mut self, text: impl Into<String>) -> Self {
12133            self.spec.text = Some(text.into());
12134            self
12135        }
12136
12137        const fn with_channel(mut self, channel: i32) -> Self {
12138            self.spec.channel = channel;
12139            self
12140        }
12141
12142        const fn with_span(mut self, start: usize, stop: usize) -> Self {
12143            self.spec.start = start;
12144            self.spec.stop = stop;
12145            self.spec.start_byte = start;
12146            self.spec.stop_byte = match stop.checked_add(1) {
12147                Some(end) if end >= start => end,
12148                Some(_) | None => start,
12149            };
12150            self
12151        }
12152
12153        const fn with_position(mut self, line: usize, column: usize) -> Self {
12154            self.spec.line = line;
12155            self.spec.column = column;
12156            self
12157        }
12158
12159        fn set_token_index(&mut self, index: isize) {
12160            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
12161        }
12162    }
12163
12164    impl Token for TestToken {
12165        fn token_id(&self) -> TokenId {
12166            self.id
12167        }
12168
12169        fn token_type(&self) -> i32 {
12170            self.spec.token_type
12171        }
12172
12173        fn channel(&self) -> i32 {
12174            self.spec.channel
12175        }
12176
12177        fn start(&self) -> usize {
12178            self.spec.start
12179        }
12180
12181        fn stop(&self) -> usize {
12182            self.spec.stop
12183        }
12184
12185        fn line(&self) -> usize {
12186            self.spec.line
12187        }
12188
12189        fn column(&self) -> usize {
12190            self.spec.column
12191        }
12192
12193        fn text(&self) -> Option<&str> {
12194            self.spec.text.as_deref()
12195        }
12196
12197        fn source_name(&self) -> &str {
12198            &self.source_name
12199        }
12200
12201        fn start_byte(&self) -> usize {
12202            self.spec.start_byte
12203        }
12204
12205        fn stop_byte(&self) -> usize {
12206            self.spec.stop_byte
12207        }
12208    }
12209
12210    #[derive(Debug)]
12211    struct Source {
12212        tokens: Vec<TestToken>,
12213        index: usize,
12214    }
12215
12216    impl TokenSource for Source {
12217        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12218            let token = self
12219                .tokens
12220                .get(self.index)
12221                .cloned()
12222                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
12223            self.index += 1;
12224            sink.push(token.spec)
12225        }
12226
12227        fn line(&self) -> usize {
12228            1
12229        }
12230
12231        fn column(&self) -> usize {
12232            self.index
12233        }
12234
12235        fn source_name(&self) -> &'static str {
12236            "parser-test"
12237        }
12238    }
12239
12240    #[derive(Clone, Debug, Eq, PartialEq)]
12241    struct RecordedDiagnostic {
12242        grammar_file_name: String,
12243        line: usize,
12244        column: usize,
12245        message: String,
12246        error: Option<AntlrError>,
12247    }
12248
12249    #[derive(Clone, Debug)]
12250    struct RecordingErrorListener {
12251        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
12252    }
12253
12254    impl<R> crate::ErrorListener<R> for RecordingErrorListener
12255    where
12256        R: Recognizer + ?Sized,
12257    {
12258        fn syntax_error(
12259            &mut self,
12260            recognizer: &R,
12261            line: usize,
12262            column: usize,
12263            message: &str,
12264            error: Option<&AntlrError>,
12265        ) {
12266            self.diagnostics
12267                .lock()
12268                .expect("recorded diagnostics lock")
12269                .push(RecordedDiagnostic {
12270                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
12271                    line,
12272                    column,
12273                    message: message.to_owned(),
12274                    error: error.cloned(),
12275                });
12276        }
12277    }
12278
12279    #[derive(Debug)]
12280    struct ReportingSource {
12281        source: Source,
12282        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
12283    }
12284
12285    impl TokenSource for ReportingSource {
12286        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
12287            self.source.next_token(sink)
12288        }
12289
12290        fn line(&self) -> usize {
12291            self.source.line()
12292        }
12293
12294        fn column(&self) -> usize {
12295            self.source.column()
12296        }
12297
12298        fn source_name(&self) -> &str {
12299            self.source.source_name()
12300        }
12301
12302        fn report_error(&self, error: &TokenSourceError) -> bool {
12303            self.diagnostics.borrow_mut().push(error.clone());
12304            true
12305        }
12306    }
12307
12308    fn mini_parser_data() -> RecognizerData {
12309        RecognizerData::new(
12310            "Mini.g4",
12311            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12312        )
12313        .with_rule_names(["s"])
12314    }
12315
12316    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
12317        let data = mini_parser_data();
12318        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
12319    }
12320
12321    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
12322    where
12323        H: SemanticHooks,
12324    {
12325        BaseParser::with_semantic_hooks(
12326            CommonTokenStream::new(Source { tokens, index: 0 }),
12327            mini_parser_data(),
12328            hooks,
12329        )
12330    }
12331
12332    #[test]
12333    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
12334        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
12335        parser.remove_error_listeners();
12336        let diagnostics = Arc::new(Mutex::new(Vec::new()));
12337        parser.add_error_listener(RecordingErrorListener {
12338            diagnostics: Arc::clone(&diagnostics),
12339        });
12340        let parser_diagnostics = [ParserDiagnostic {
12341            line: 1,
12342            column: 2,
12343            message: "missing 'x' at 'y'".to_owned(),
12344        }];
12345        let token_errors = [
12346            TokenSourceError::new(1, 1, "token recognition error at: '@'"),
12347            TokenSourceError::new(1, 3, "token recognition error at: '#'"),
12348        ];
12349
12350        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12351
12352        assert_eq!(
12353            *diagnostics.lock().expect("recorded diagnostics lock"),
12354            [
12355                RecordedDiagnostic {
12356                    grammar_file_name: "Mini.g4".to_owned(),
12357                    line: 1,
12358                    column: 1,
12359                    message: "token recognition error at: '@'".to_owned(),
12360                    error: None,
12361                },
12362                RecordedDiagnostic {
12363                    grammar_file_name: "Mini.g4".to_owned(),
12364                    line: 1,
12365                    column: 2,
12366                    message: "missing 'x' at 'y'".to_owned(),
12367                    error: None,
12368                },
12369                RecordedDiagnostic {
12370                    grammar_file_name: "Mini.g4".to_owned(),
12371                    line: 1,
12372                    column: 3,
12373                    message: "token recognition error at: '#'".to_owned(),
12374                    error: None,
12375                },
12376            ]
12377        );
12378
12379        parser.remove_error_listeners();
12380        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
12381        assert_eq!(
12382            diagnostics.lock().expect("recorded diagnostics lock").len(),
12383            3
12384        );
12385    }
12386
12387    #[test]
12388    fn parser_leaves_token_errors_to_source_owned_listeners() {
12389        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
12390        let source = ReportingSource {
12391            source: Source {
12392                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
12393                index: 0,
12394            },
12395            diagnostics: Rc::clone(&source_diagnostics),
12396        };
12397        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
12398        parser.remove_error_listeners();
12399        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
12400        parser.add_error_listener(RecordingErrorListener {
12401            diagnostics: Arc::clone(&parser_diagnostics),
12402        });
12403        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
12404
12405        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
12406
12407        assert_eq!(*source_diagnostics.borrow(), [source_error]);
12408        assert!(
12409            parser_diagnostics
12410                .lock()
12411                .expect("recorded diagnostics lock")
12412                .is_empty()
12413        );
12414    }
12415
12416    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
12417        builder.finish().expect("valid packed parser ATN")
12418    }
12419
12420    fn ordinary_star_loop_atn() -> Atn {
12421        let mut atn = ParserAtnBuilder::new(2);
12422        for (state_number, kind, rule_index) in [
12423            (0, AtnStateKind::RuleStart, 0),
12424            (1, AtnStateKind::StarLoopEntry, 0),
12425            (2, AtnStateKind::Basic, 0),
12426            (3, AtnStateKind::StarLoopBack, 0),
12427            (4, AtnStateKind::LoopEnd, 0),
12428            (5, AtnStateKind::Basic, 0),
12429            (6, AtnStateKind::RuleStop, 0),
12430            (7, AtnStateKind::RuleStart, 1),
12431            (8, AtnStateKind::Basic, 1),
12432            (9, AtnStateKind::RuleStop, 1),
12433        ] {
12434            assert_eq!(
12435                atn.add_state(kind, Some(rule_index))
12436                    .expect("state")
12437                    .index(),
12438                state_number
12439            );
12440        }
12441        atn.set_rule_to_start_state(vec![0, 7])
12442            .expect("rule start states");
12443        atn.set_rule_to_stop_state(vec![6, 9])
12444            .expect("rule stop states");
12445        atn.add_decision_state(1).expect("decision state");
12446        atn.set_loop_back_state(4, 3).expect("loop back state");
12447        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12448            .expect("transition");
12449        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12450            .expect("transition");
12451        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12452            .expect("transition");
12453        atn.add_transition(
12454            2,
12455            ParserTransitionSpec::Rule {
12456                target: 7,
12457                rule_index: 1,
12458                follow_state: 3,
12459                precedence: 0,
12460            },
12461        )
12462        .expect("transition");
12463        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12464            .expect("transition");
12465        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12466            .expect("transition");
12467        atn.add_transition(
12468            5,
12469            ParserTransitionSpec::Atom {
12470                target: 6,
12471                label: TOKEN_EOF,
12472            },
12473        )
12474        .expect("transition");
12475        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12476            .expect("transition");
12477        atn.add_transition(
12478            8,
12479            ParserTransitionSpec::Atom {
12480                target: 9,
12481                label: 1,
12482            },
12483        )
12484        .expect("transition");
12485        finish_atn(atn)
12486    }
12487
12488    /// ATN for `s : (X | X X)* EOF`.
12489    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12490        let mut atn = ParserAtnBuilder::new(1);
12491        for (state_number, kind) in [
12492            (0, AtnStateKind::RuleStart),
12493            (1, AtnStateKind::StarLoopEntry),
12494            (2, AtnStateKind::StarBlockStart),
12495            (3, AtnStateKind::Basic),
12496            (4, AtnStateKind::BlockEnd),
12497            (5, AtnStateKind::StarLoopBack),
12498            (6, AtnStateKind::LoopEnd),
12499            (7, AtnStateKind::Basic),
12500            (8, AtnStateKind::RuleStop),
12501        ] {
12502            assert_eq!(
12503                atn.add_state(kind, Some(0)).expect("state").index(),
12504                state_number
12505            );
12506        }
12507        atn.set_rule_to_start_state(vec![0])
12508            .expect("rule start states");
12509        atn.set_rule_to_stop_state(vec![8])
12510            .expect("rule stop states");
12511        atn.set_end_state(2, 4).expect("block end state");
12512        atn.set_loop_back_state(6, 5).expect("loop back state");
12513        atn.add_decision_state(1).expect("decision state");
12514        atn.add_decision_state(2).expect("decision state");
12515        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12516            .expect("transition");
12517        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12518            .expect("transition");
12519        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
12520            .expect("transition");
12521        atn.add_transition(
12522            2,
12523            ParserTransitionSpec::Atom {
12524                target: 4,
12525                label: 1,
12526            },
12527        )
12528        .expect("transition");
12529        atn.add_transition(
12530            2,
12531            ParserTransitionSpec::Atom {
12532                target: 3,
12533                label: 1,
12534            },
12535        )
12536        .expect("transition");
12537        atn.add_transition(
12538            3,
12539            ParserTransitionSpec::Atom {
12540                target: 4,
12541                label: 1,
12542            },
12543        )
12544        .expect("transition");
12545        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12546            .expect("transition");
12547        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
12548            .expect("transition");
12549        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12550            .expect("transition");
12551        atn.add_transition(
12552            7,
12553            ParserTransitionSpec::Atom {
12554                target: 8,
12555                label: TOKEN_EOF,
12556            },
12557        )
12558        .expect("transition");
12559        finish_atn(atn)
12560    }
12561
12562    fn ordinary_plus_loop_atn() -> Atn {
12563        let mut atn = ParserAtnBuilder::new(2);
12564        for (state_number, kind, rule_index) in [
12565            (0, AtnStateKind::RuleStart, 0),
12566            (1, AtnStateKind::Basic, 0),
12567            (2, AtnStateKind::PlusLoopBack, 0),
12568            (3, AtnStateKind::LoopEnd, 0),
12569            (4, AtnStateKind::Basic, 0),
12570            (5, AtnStateKind::RuleStop, 0),
12571            (6, AtnStateKind::RuleStart, 1),
12572            (7, AtnStateKind::Basic, 1),
12573            (8, AtnStateKind::RuleStop, 1),
12574        ] {
12575            assert_eq!(
12576                atn.add_state(kind, Some(rule_index))
12577                    .expect("state")
12578                    .index(),
12579                state_number
12580            );
12581        }
12582        atn.set_rule_to_start_state(vec![0, 6])
12583            .expect("rule start states");
12584        atn.set_rule_to_stop_state(vec![5, 8])
12585            .expect("rule stop states");
12586        atn.add_decision_state(2).expect("decision state");
12587        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12588            .expect("transition");
12589        atn.add_transition(
12590            1,
12591            ParserTransitionSpec::Rule {
12592                target: 6,
12593                rule_index: 1,
12594                follow_state: 2,
12595                precedence: 0,
12596            },
12597        )
12598        .expect("transition");
12599        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
12600            .expect("transition");
12601        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12602            .expect("transition");
12603        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
12604            .expect("transition");
12605        atn.add_transition(
12606            4,
12607            ParserTransitionSpec::Atom {
12608                target: 5,
12609                label: TOKEN_EOF,
12610            },
12611        )
12612        .expect("transition");
12613        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12614            .expect("transition");
12615        atn.add_transition(
12616            7,
12617            ParserTransitionSpec::Atom {
12618                target: 8,
12619                label: 1,
12620            },
12621        )
12622        .expect("transition");
12623        finish_atn(atn)
12624    }
12625
12626    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
12627        let mut tokens = (0..count)
12628            .map(|_| TestToken::new(1).with_text("x"))
12629            .collect::<Vec<_>>();
12630        tokens.push(TestToken::eof("parser-test", count, 1, count));
12631        tokens
12632    }
12633
12634    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
12635        let mut atn = ParserAtnBuilder::new(2);
12636        assert_eq!(
12637            atn.add_state(AtnStateKind::RuleStart, Some(0))
12638                .expect("state")
12639                .index(),
12640            0
12641        );
12642        assert_eq!(
12643            atn.add_state(AtnStateKind::Basic, Some(0))
12644                .expect("state")
12645                .index(),
12646            1
12647        );
12648        assert_eq!(
12649            atn.add_state(AtnStateKind::Basic, Some(0))
12650                .expect("state")
12651                .index(),
12652            2
12653        );
12654        assert_eq!(
12655            atn.add_state(AtnStateKind::RuleStart, Some(1))
12656                .expect("state")
12657                .index(),
12658            3
12659        );
12660        atn.set_left_recursive_rule(3)
12661            .expect("left-recursive rule start");
12662        assert_eq!(
12663            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
12664                .expect("state")
12665                .index(),
12666            4
12667        );
12668        atn.set_precedence_rule_decision(4)
12669            .expect("precedence decision");
12670        assert_eq!(
12671            atn.add_state(AtnStateKind::Basic, Some(1))
12672                .expect("state")
12673                .index(),
12674            5
12675        );
12676        assert_eq!(
12677            atn.add_state(AtnStateKind::Basic, Some(1))
12678                .expect("state")
12679                .index(),
12680            6
12681        );
12682        assert_eq!(
12683            atn.add_state(AtnStateKind::LoopEnd, Some(1))
12684                .expect("state")
12685                .index(),
12686            7
12687        );
12688        assert_eq!(
12689            atn.add_state(AtnStateKind::RuleStop, Some(1))
12690                .expect("state")
12691                .index(),
12692            8
12693        );
12694        assert_eq!(
12695            atn.add_state(AtnStateKind::RuleStop, Some(0))
12696                .expect("state")
12697                .index(),
12698            9
12699        );
12700        atn.set_rule_to_start_state(vec![0, 3])
12701            .expect("rule start states");
12702        atn.set_rule_to_stop_state(vec![9, 8])
12703            .expect("rule stop states");
12704        atn.add_transition(
12705            1,
12706            ParserTransitionSpec::Rule {
12707                target: 3,
12708                rule_index: 1,
12709                follow_state: 2,
12710                precedence: 0,
12711            },
12712        )
12713        .expect("transition");
12714        atn.add_transition(
12715            2,
12716            ParserTransitionSpec::Atom {
12717                target: 9,
12718                label: caller_symbol,
12719            },
12720        )
12721        .expect("transition");
12722        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12723            .expect("transition");
12724        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
12725            .expect("transition");
12726        atn.add_transition(
12727            5,
12728            ParserTransitionSpec::Precedence {
12729                target: 6,
12730                precedence: 1,
12731            },
12732        )
12733        .expect("transition");
12734        atn.add_transition(
12735            6,
12736            ParserTransitionSpec::Atom {
12737                target: 4,
12738                label: 1,
12739            },
12740        )
12741        .expect("transition");
12742        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12743            .expect("transition");
12744        finish_atn(atn)
12745    }
12746
12747    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
12748        let mut parser = mini_parser(vec![
12749            TestToken::new(symbol).with_text("lookahead"),
12750            TestToken::eof("parser-test", 1, 1, 1),
12751        ]);
12752        parser.rule_context_stack = vec![
12753            RuleContextFrame {
12754                rule_index: 0,
12755                invoking_state: -1,
12756            },
12757            RuleContextFrame {
12758                rule_index: 1,
12759                invoking_state: 1,
12760            },
12761        ];
12762        parser
12763    }
12764
12765    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
12766        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
12767        //   prec 2: token 1, token 1  (shift `>>`)
12768        //   prec 1: token 1           (relational `>`)
12769        let mut atn = ParserAtnBuilder::new(1);
12770        for (state, kind, rule) in [
12771            (0, AtnStateKind::RuleStart, 0),
12772            (1, AtnStateKind::StarLoopEntry, 0),
12773            (2, AtnStateKind::Basic, 0), // ops hub
12774            (3, AtnStateKind::Basic, 0), // shift prec
12775            (4, AtnStateKind::Basic, 0), // shift first >
12776            (5, AtnStateKind::Basic, 0), // shift second >
12777            (6, AtnStateKind::Basic, 0), // rel prec
12778            (7, AtnStateKind::Basic, 0), // rel >
12779            (8, AtnStateKind::LoopEnd, 0),
12780            (9, AtnStateKind::RuleStop, 0),
12781        ] {
12782            assert_eq!(
12783                atn.add_state(kind, Some(rule)).expect("state").index(),
12784                state
12785            );
12786            if state == 0 {
12787                atn.set_left_recursive_rule(state)
12788                    .expect("left-recursive rule start");
12789            } else if state == 1 {
12790                atn.set_precedence_rule_decision(state)
12791                    .expect("precedence decision");
12792            }
12793        }
12794        atn.set_rule_to_start_state(vec![0])
12795            .expect("rule start states");
12796        atn.set_rule_to_stop_state(vec![9])
12797            .expect("rule stop states");
12798        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12799            .expect("ops");
12800        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12801            .expect("exit");
12802        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12803            .expect("to shift");
12804        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12805            .expect("to rel");
12806        atn.add_transition(
12807            3,
12808            ParserTransitionSpec::Precedence {
12809                target: 4,
12810                precedence: 2,
12811            },
12812        )
12813        .expect("shift prec");
12814        atn.add_transition(
12815            4,
12816            ParserTransitionSpec::Atom {
12817                target: 5,
12818                label: 1,
12819            },
12820        )
12821        .expect("shift first >");
12822        atn.add_transition(
12823            5,
12824            ParserTransitionSpec::Atom {
12825                target: 1,
12826                label: 1,
12827            },
12828        )
12829        .expect("shift second >");
12830        atn.add_transition(
12831            6,
12832            ParserTransitionSpec::Precedence {
12833                target: 7,
12834                precedence: 1,
12835            },
12836        )
12837        .expect("rel prec");
12838        atn.add_transition(
12839            7,
12840            ParserTransitionSpec::Atom {
12841                target: 1,
12842                label: 1,
12843            },
12844        )
12845        .expect("rel >");
12846        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12847            .expect("loop end");
12848        finish_atn(atn)
12849    }
12850
12851    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
12852        let mut atn = ParserAtnBuilder::new(2);
12853        for (state, kind, rule) in [
12854            (0, AtnStateKind::RuleStart, 0),
12855            (1, AtnStateKind::StarLoopEntry, 0),
12856            (2, AtnStateKind::Basic, 0),
12857            (3, AtnStateKind::Basic, 0),
12858            (4, AtnStateKind::Basic, 0),
12859            (5, AtnStateKind::Basic, 0),
12860            (6, AtnStateKind::Basic, 0),
12861            (7, AtnStateKind::Basic, 0),
12862            (8, AtnStateKind::LoopEnd, 0),
12863            (9, AtnStateKind::RuleStop, 0),
12864            (10, AtnStateKind::RuleStart, 1),
12865            (11, AtnStateKind::Basic, 1),
12866            (12, AtnStateKind::RuleStop, 1),
12867        ] {
12868            assert_eq!(
12869                atn.add_state(kind, Some(rule)).expect("state").index(),
12870                state
12871            );
12872            if state == 0 {
12873                atn.set_left_recursive_rule(state)
12874                    .expect("left-recursive rule start");
12875            } else if state == 1 {
12876                atn.set_precedence_rule_decision(state)
12877                    .expect("precedence decision");
12878            }
12879        }
12880        atn.set_rule_to_start_state(vec![0, 10])
12881            .expect("rule start states");
12882        atn.set_rule_to_stop_state(vec![9, 12])
12883            .expect("rule stop states");
12884        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12885            .expect("ops");
12886        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12887            .expect("exit");
12888        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12889            .expect("to shift");
12890        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12891            .expect("to relational");
12892        atn.add_transition(
12893            3,
12894            ParserTransitionSpec::Precedence {
12895                target: 4,
12896                precedence: 2,
12897            },
12898        )
12899        .expect("shift precedence");
12900        atn.add_transition(
12901            4,
12902            ParserTransitionSpec::Rule {
12903                target: 10,
12904                rule_index: 1,
12905                follow_state: 5,
12906                precedence: 0,
12907            },
12908        )
12909        .expect("first shift token helper");
12910        atn.add_transition(
12911            5,
12912            ParserTransitionSpec::Atom {
12913                target: 1,
12914                label: 1,
12915            },
12916        )
12917        .expect("second shift token");
12918        atn.add_transition(
12919            6,
12920            ParserTransitionSpec::Precedence {
12921                target: 7,
12922                precedence: 1,
12923            },
12924        )
12925        .expect("relational precedence");
12926        atn.add_transition(
12927            7,
12928            ParserTransitionSpec::Atom {
12929                target: 1,
12930                label: 1,
12931            },
12932        )
12933        .expect("relational token");
12934        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12935            .expect("loop end");
12936        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
12937            .expect("helper entry");
12938        atn.add_transition(
12939            11,
12940            ParserTransitionSpec::Atom {
12941                target: 12,
12942                label: 1,
12943            },
12944        )
12945        .expect("first shift token");
12946        finish_atn(atn)
12947    }
12948
12949    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
12950        let mut atn = ParserAtnBuilder::new(1);
12951        for (state, kind) in [
12952            (0, AtnStateKind::RuleStart),
12953            (1, AtnStateKind::StarLoopEntry),
12954            (2, AtnStateKind::Basic),
12955            (3, AtnStateKind::Basic),
12956            (4, AtnStateKind::Basic),
12957            (5, AtnStateKind::Basic),
12958            (6, AtnStateKind::Basic),
12959            (7, AtnStateKind::Basic),
12960            (8, AtnStateKind::Basic),
12961            (9, AtnStateKind::LoopEnd),
12962            (10, AtnStateKind::RuleStop),
12963        ] {
12964            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
12965            if state == 0 {
12966                atn.set_left_recursive_rule(state)
12967                    .expect("left-recursive rule start");
12968            } else if state == 1 {
12969                atn.set_precedence_rule_decision(state)
12970                    .expect("precedence decision");
12971            }
12972        }
12973        atn.set_rule_to_start_state(vec![0])
12974            .expect("rule start states");
12975        atn.set_rule_to_stop_state(vec![10])
12976            .expect("rule stop states");
12977        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12978            .expect("ops");
12979        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
12980            .expect("exit");
12981        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12982            .expect("to multi-token operator");
12983        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12984            .expect("to predicate operator");
12985        atn.add_transition(
12986            3,
12987            ParserTransitionSpec::Precedence {
12988                target: 4,
12989                precedence: 2,
12990            },
12991        )
12992        .expect("multi-token precedence");
12993        atn.add_transition(
12994            4,
12995            ParserTransitionSpec::Atom {
12996                target: 5,
12997                label: 1,
12998            },
12999        )
13000        .expect("multi-token first");
13001        atn.add_transition(
13002            5,
13003            ParserTransitionSpec::Atom {
13004                target: 1,
13005                label: 1,
13006            },
13007        )
13008        .expect("multi-token second");
13009        atn.add_transition(
13010            6,
13011            ParserTransitionSpec::Precedence {
13012                target: 7,
13013                precedence: 2,
13014            },
13015        )
13016        .expect("predicate precedence");
13017        atn.add_transition(
13018            7,
13019            ParserTransitionSpec::Predicate {
13020                target: 8,
13021                rule_index: 0,
13022                pred_index: 0,
13023                context_dependent: false,
13024            },
13025        )
13026        .expect("operator predicate");
13027        atn.add_transition(
13028            8,
13029            ParserTransitionSpec::Atom {
13030                target: 1,
13031                label: 1,
13032            },
13033        )
13034        .expect("predicate single token");
13035        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13036            .expect("loop end");
13037        finish_atn(atn)
13038    }
13039
13040    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
13041        let mut atn = ParserAtnBuilder::new(2);
13042        for (state, kind, rule) in [
13043            (0, AtnStateKind::RuleStart, 0),
13044            (1, AtnStateKind::StarLoopEntry, 0),
13045            (2, AtnStateKind::Basic, 0),
13046            (3, AtnStateKind::Basic, 0),
13047            (4, AtnStateKind::Basic, 0),
13048            (5, AtnStateKind::LoopEnd, 0),
13049            (6, AtnStateKind::RuleStop, 0),
13050            (7, AtnStateKind::RuleStart, 1),
13051            (8, AtnStateKind::RuleStop, 1),
13052            (9, AtnStateKind::Basic, 1),
13053        ] {
13054            assert_eq!(
13055                atn.add_state(kind, Some(rule)).expect("state").index(),
13056                state
13057            );
13058            if state == 0 {
13059                atn.set_left_recursive_rule(state)
13060                    .expect("left-recursive rule start");
13061            } else if state == 1 {
13062                atn.set_precedence_rule_decision(state)
13063                    .expect("precedence decision");
13064            }
13065        }
13066        atn.set_rule_to_start_state(vec![0, 7])
13067            .expect("rule start states");
13068        atn.set_rule_to_stop_state(vec![6, 8])
13069            .expect("rule stop states");
13070        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13071            .expect("transition");
13072        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13073            .expect("transition");
13074        atn.add_transition(
13075            2,
13076            ParserTransitionSpec::Precedence {
13077                target: 3,
13078                precedence: 3,
13079            },
13080        )
13081        .expect("transition");
13082        atn.add_transition(
13083            3,
13084            ParserTransitionSpec::Rule {
13085                target: 7,
13086                rule_index: 1,
13087                follow_state: 4,
13088                precedence: 0,
13089            },
13090        )
13091        .expect("transition");
13092        atn.add_transition(
13093            4,
13094            ParserTransitionSpec::Atom {
13095                target: 1,
13096                label: 1,
13097            },
13098        )
13099        .expect("transition");
13100        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13101            .expect("transition");
13102        atn.add_transition(
13103            7,
13104            ParserTransitionSpec::Precedence {
13105                target: 9,
13106                precedence: 1,
13107            },
13108        )
13109        .expect("transition");
13110        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
13111            .expect("transition");
13112        finish_atn(atn)
13113    }
13114
13115    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
13116        let mut atn = ParserAtnBuilder::new(2);
13117        for (state, kind) in [
13118            (0, AtnStateKind::RuleStart),
13119            (1, AtnStateKind::StarLoopEntry),
13120            (2, AtnStateKind::Basic),
13121            (3, AtnStateKind::Basic),
13122            (4, AtnStateKind::Basic),
13123            (5, AtnStateKind::LoopEnd),
13124            (6, AtnStateKind::RuleStop),
13125        ] {
13126            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13127            if state == 0 {
13128                atn.set_left_recursive_rule(state)
13129                    .expect("left-recursive rule start");
13130            } else if state == 1 {
13131                atn.set_precedence_rule_decision(state)
13132                    .expect("precedence decision");
13133            }
13134        }
13135        atn.set_rule_to_start_state(vec![0])
13136            .expect("rule start states");
13137        atn.set_rule_to_stop_state(vec![6])
13138            .expect("rule stop states");
13139        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13140            .expect("transition");
13141        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13142            .expect("transition");
13143        atn.add_transition(
13144            2,
13145            ParserTransitionSpec::Precedence {
13146                target: 3,
13147                precedence: 1,
13148            },
13149        )
13150        .expect("transition");
13151        atn.add_transition(
13152            3,
13153            ParserTransitionSpec::Predicate {
13154                target: 4,
13155                rule_index: 0,
13156                pred_index: 0,
13157                context_dependent: false,
13158            },
13159        )
13160        .expect("transition");
13161        atn.add_transition(
13162            4,
13163            ParserTransitionSpec::Atom {
13164                target: 1,
13165                label: 1,
13166            },
13167        )
13168        .expect("transition");
13169        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13170            .expect("transition");
13171        finish_atn(atn)
13172    }
13173
13174    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
13175        let mut atn = ParserAtnBuilder::new(2);
13176        for (state, kind, rule) in [
13177            (0, AtnStateKind::RuleStart, 0),
13178            (1, AtnStateKind::Basic, 0),
13179            (2, AtnStateKind::Basic, 0),
13180            (3, AtnStateKind::Basic, 0),
13181            (4, AtnStateKind::RuleStop, 0),
13182            (5, AtnStateKind::RuleStart, 1),
13183            (6, AtnStateKind::StarLoopEntry, 1),
13184            (7, AtnStateKind::Basic, 1),
13185            (8, AtnStateKind::Basic, 1),
13186            (9, AtnStateKind::LoopEnd, 1),
13187            (10, AtnStateKind::RuleStop, 1),
13188            (11, AtnStateKind::RuleStart, 2),
13189            (12, AtnStateKind::RuleStop, 2),
13190        ] {
13191            assert_eq!(
13192                atn.add_state(kind, Some(rule)).expect("state").index(),
13193                state
13194            );
13195            if state == 5 {
13196                atn.set_left_recursive_rule(state)
13197                    .expect("left-recursive rule start");
13198            } else if state == 6 {
13199                atn.set_precedence_rule_decision(state)
13200                    .expect("precedence decision");
13201            }
13202        }
13203        atn.set_rule_to_start_state(vec![0, 5, 11])
13204            .expect("rule start states");
13205        atn.set_rule_to_stop_state(vec![4, 10, 12])
13206            .expect("rule stop states");
13207        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13208            .expect("transition");
13209        atn.add_transition(
13210            1,
13211            ParserTransitionSpec::Rule {
13212                target: 5,
13213                rule_index: 1,
13214                follow_state: 2,
13215                precedence: 0,
13216            },
13217        )
13218        .expect("transition");
13219        atn.add_transition(
13220            2,
13221            ParserTransitionSpec::Rule {
13222                target: 11,
13223                rule_index: 2,
13224                follow_state: 3,
13225                precedence: 0,
13226            },
13227        )
13228        .expect("transition");
13229        atn.add_transition(
13230            3,
13231            ParserTransitionSpec::Atom {
13232                target: 4,
13233                label: caller_symbol,
13234            },
13235        )
13236        .expect("transition");
13237        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13238            .expect("transition");
13239        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
13240            .expect("transition");
13241        atn.add_transition(
13242            7,
13243            ParserTransitionSpec::Precedence {
13244                target: 8,
13245                precedence: 1,
13246            },
13247        )
13248        .expect("transition");
13249        atn.add_transition(
13250            8,
13251            ParserTransitionSpec::Atom {
13252                target: 6,
13253                label: 1,
13254            },
13255        )
13256        .expect("transition");
13257        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13258            .expect("transition");
13259        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
13260            .expect("transition");
13261        finish_atn(atn)
13262    }
13263
13264    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
13265        let mut atn = ParserAtnBuilder::new(2);
13266        for (state, kind, rule) in [
13267            (0, AtnStateKind::RuleStart, 0),
13268            (1, AtnStateKind::Basic, 0),
13269            (2, AtnStateKind::Basic, 0),
13270            (3, AtnStateKind::RuleStop, 0),
13271            (4, AtnStateKind::RuleStart, 1),
13272            (5, AtnStateKind::Basic, 1),
13273            (6, AtnStateKind::Basic, 1),
13274            (7, AtnStateKind::RuleStop, 1),
13275            (8, AtnStateKind::RuleStart, 2),
13276            (9, AtnStateKind::StarLoopEntry, 2),
13277            (10, AtnStateKind::Basic, 2),
13278            (11, AtnStateKind::Basic, 2),
13279            (12, AtnStateKind::LoopEnd, 2),
13280            (13, AtnStateKind::RuleStop, 2),
13281        ] {
13282            assert_eq!(
13283                atn.add_state(kind, Some(rule)).expect("state").index(),
13284                state
13285            );
13286            if state == 8 {
13287                atn.set_left_recursive_rule(state)
13288                    .expect("left-recursive rule start");
13289            } else if state == 9 {
13290                atn.set_precedence_rule_decision(state)
13291                    .expect("precedence decision");
13292            }
13293        }
13294        atn.set_rule_to_start_state(vec![0, 4, 8])
13295            .expect("rule start states");
13296        atn.set_rule_to_stop_state(vec![3, 7, 13])
13297            .expect("rule stop states");
13298        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13299            .expect("transition");
13300        atn.add_transition(
13301            1,
13302            ParserTransitionSpec::Rule {
13303                target: 4,
13304                rule_index: 1,
13305                follow_state: 2,
13306                precedence: 0,
13307            },
13308        )
13309        .expect("transition");
13310        atn.add_transition(
13311            2,
13312            ParserTransitionSpec::Atom {
13313                target: 3,
13314                label: caller_symbol,
13315            },
13316        )
13317        .expect("transition");
13318        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13319            .expect("transition");
13320        atn.add_transition(
13321            5,
13322            ParserTransitionSpec::Rule {
13323                target: 8,
13324                rule_index: 2,
13325                follow_state: 6,
13326                precedence: 0,
13327            },
13328        )
13329        .expect("transition");
13330        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13331            .expect("transition");
13332        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13333            .expect("transition");
13334        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
13335            .expect("transition");
13336        atn.add_transition(
13337            10,
13338            ParserTransitionSpec::Precedence {
13339                target: 11,
13340                precedence: 1,
13341            },
13342        )
13343        .expect("transition");
13344        atn.add_transition(
13345            11,
13346            ParserTransitionSpec::Atom {
13347                target: 9,
13348                label: 1,
13349            },
13350        )
13351        .expect("transition");
13352        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
13353            .expect("transition");
13354        finish_atn(atn)
13355    }
13356
13357    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
13358        let mut atn = ParserAtnBuilder::new(2);
13359        for (state, kind, rule) in [
13360            (0, AtnStateKind::RuleStart, 0),
13361            (1, AtnStateKind::Basic, 0),
13362            (2, AtnStateKind::Basic, 0),
13363            (3, AtnStateKind::RuleStop, 0),
13364            (4, AtnStateKind::RuleStart, 1),
13365            (5, AtnStateKind::StarLoopEntry, 1),
13366            (6, AtnStateKind::Basic, 1),
13367            (7, AtnStateKind::Basic, 1),
13368            (8, AtnStateKind::Basic, 1),
13369            (9, AtnStateKind::Basic, 1),
13370            (10, AtnStateKind::LoopEnd, 1),
13371            (11, AtnStateKind::RuleStop, 1),
13372        ] {
13373            assert_eq!(
13374                atn.add_state(kind, Some(rule)).expect("state").index(),
13375                state
13376            );
13377            if state == 4 {
13378                atn.set_left_recursive_rule(state)
13379                    .expect("left-recursive rule start");
13380            } else if state == 5 {
13381                atn.set_precedence_rule_decision(state)
13382                    .expect("precedence decision");
13383            }
13384        }
13385        atn.set_rule_to_start_state(vec![0, 4])
13386            .expect("rule start states");
13387        atn.set_rule_to_stop_state(vec![3, 11])
13388            .expect("rule stop states");
13389        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13390            .expect("transition");
13391        atn.add_transition(
13392            1,
13393            ParserTransitionSpec::Rule {
13394                target: 4,
13395                rule_index: 1,
13396                follow_state: 2,
13397                precedence: 0,
13398            },
13399        )
13400        .expect("transition");
13401        atn.add_transition(
13402            2,
13403            ParserTransitionSpec::Atom {
13404                target: 3,
13405                label: caller_symbol,
13406            },
13407        )
13408        .expect("transition");
13409        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13410            .expect("transition");
13411        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
13412            .expect("transition");
13413        atn.add_transition(
13414            6,
13415            ParserTransitionSpec::Precedence {
13416                target: 7,
13417                precedence: 1,
13418            },
13419        )
13420        .expect("transition");
13421        atn.add_transition(
13422            7,
13423            ParserTransitionSpec::Atom {
13424                target: 8,
13425                label: 1,
13426            },
13427        )
13428        .expect("transition");
13429        atn.add_transition(
13430            8,
13431            ParserTransitionSpec::Rule {
13432                target: 4,
13433                rule_index: 1,
13434                follow_state: 9,
13435                precedence: 2,
13436            },
13437        )
13438        .expect("transition");
13439        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13440            .expect("transition");
13441        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13442            .expect("transition");
13443        finish_atn(atn)
13444    }
13445
13446    #[test]
13447    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13448        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13449        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13450
13451        let mut overlapping = parser_inside_left_recursive_callee(1);
13452        assert_eq!(
13453            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13454            None
13455        );
13456
13457        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13458        assert_eq!(
13459            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13460            Some(true)
13461        );
13462
13463        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13464        assert_eq!(
13465            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13466            Some(false)
13467        );
13468
13469        assert_eq!(
13470            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13471            Some(true),
13472            "overlap results must not leak across ATNs"
13473        );
13474    }
13475
13476    #[test]
13477    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
13478        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
13479        let mut parser = mini_parser(vec![
13480            TestToken::new(1).with_text("operator"),
13481            TestToken::eof("parser-test", 1, 1, 1),
13482        ]);
13483        parser.rule_context_stack = vec![RuleContextFrame {
13484            rule_index: 0,
13485            invoking_state: -1,
13486        }];
13487
13488        assert_eq!(
13489            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13490            Some(true)
13491        );
13492        assert_eq!(
13493            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13494            Some(true),
13495            "cached operator lookahead must preserve the nullable prefix return path"
13496        );
13497        assert_eq!(
13498            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13499            Some(true),
13500            "the nullable child must use its rule-call precedence, not the caller precedence"
13501        );
13502    }
13503
13504    #[test]
13505    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
13506        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
13507        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
13508        // must defer so StarLoopEntry adaptive predict can exit when the second
13509        // `>` is absent (as in `a < b > c`).
13510        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
13511        let mut parser = mini_parser(vec![
13512            TestToken::new(1).with_text(">"),
13513            TestToken::new(2).with_text("id"),
13514            TestToken::eof("parser-test", 1, 1, 1),
13515        ]);
13516        parser.rule_context_stack = vec![RuleContextFrame {
13517            rule_index: 0,
13518            invoking_state: -1,
13519        }];
13520
13521        assert_eq!(
13522            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13523            Some(true),
13524            "at low precedence relational `>` is a single-token operator"
13525        );
13526        assert_eq!(
13527            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
13528            Some(true),
13529            "relational remains single-token at its own precedence"
13530        );
13531        assert_eq!(
13532            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13533            None,
13534            "at shift precedence, bare `>` must not force enter"
13535        );
13536    }
13537
13538    #[test]
13539    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
13540        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
13541        let mut parser = mini_parser(vec![
13542            TestToken::new(1).with_text(">"),
13543            TestToken::new(2).with_text("id"),
13544            TestToken::eof("parser-test", 1, 1, 1),
13545        ]);
13546        parser.rule_context_stack = vec![RuleContextFrame {
13547            rule_index: 0,
13548            invoking_state: -1,
13549        }];
13550
13551        assert_eq!(
13552            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13553            Some(true),
13554            "the direct relational alternative remains a one-token operator"
13555        );
13556        assert_eq!(
13557            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13558            None,
13559            "a token matched in the helper rule must return to the second shift token"
13560        );
13561    }
13562
13563    #[test]
13564    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
13565        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
13566        let mut parser = mini_parser(vec![
13567            TestToken::new(1).with_text(">"),
13568            TestToken::new(2).with_text("id"),
13569            TestToken::eof("parser-test", 1, 1, 1),
13570        ]);
13571        parser.rule_context_stack = vec![RuleContextFrame {
13572            rule_index: 0,
13573            invoking_state: -1,
13574        }];
13575
13576        assert_eq!(
13577            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13578            None,
13579            "a predicate-gated single-token path must not be hidden by a multi-token path"
13580        );
13581    }
13582
13583    #[test]
13584    fn left_recursive_loop_defers_predicate_guarded_operator() {
13585        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
13586        let mut parser = mini_parser_with_hooks(
13587            vec![
13588                TestToken::new(1).with_text("operator"),
13589                TestToken::eof("parser-test", 1, 1, 1),
13590            ],
13591            RejectingPredicateHooks::default(),
13592        );
13593        parser.rule_context_stack = vec![RuleContextFrame {
13594            rule_index: 0,
13595            invoking_state: -1,
13596        }];
13597
13598        assert_eq!(
13599            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13600            None,
13601            "a false predicate must be evaluated before entering the operator alternative"
13602        );
13603        assert_eq!(
13604            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13605            None,
13606            "cached predicate-dependent lookahead must keep deferring"
13607        );
13608    }
13609
13610    #[test]
13611    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
13612        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
13613        let mut parser = parser_inside_left_recursive_callee(1);
13614
13615        assert_eq!(
13616            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13617            None
13618        );
13619        assert_eq!(
13620            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13621            None,
13622            "the cached overlap must preserve the nullable child return path"
13623        );
13624    }
13625
13626    #[test]
13627    fn left_recursive_loop_defers_through_nullable_parent_return() {
13628        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
13629        let mut parser = mini_parser(vec![
13630            TestToken::new(1).with_text("lookahead"),
13631            TestToken::eof("parser-test", 1, 1, 1),
13632        ]);
13633        parser.rule_context_stack = vec![
13634            RuleContextFrame {
13635                rule_index: 0,
13636                invoking_state: -1,
13637            },
13638            RuleContextFrame {
13639                rule_index: 1,
13640                invoking_state: 1,
13641            },
13642            RuleContextFrame {
13643                rule_index: 2,
13644                invoking_state: 5,
13645            },
13646        ];
13647
13648        assert_eq!(
13649            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13650            None,
13651            "a nullable caller must unwind to its parent's consuming follow path"
13652        );
13653        assert_eq!(
13654            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13655            None,
13656            "the caller-overlap cache must not retain a false negative"
13657        );
13658    }
13659
13660    #[test]
13661    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
13662        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
13663        let mut parser = mini_parser(vec![
13664            TestToken::new(1).with_text("lookahead"),
13665            TestToken::eof("parser-test", 1, 1, 1),
13666        ]);
13667        parser.rule_context_stack = vec![
13668            RuleContextFrame {
13669                rule_index: 0,
13670                invoking_state: -1,
13671            },
13672            RuleContextFrame {
13673                rule_index: 1,
13674                invoking_state: 1,
13675            },
13676            RuleContextFrame {
13677                rule_index: 1,
13678                invoking_state: 8,
13679            },
13680        ];
13681
13682        assert_eq!(
13683            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13684            None,
13685            "a recursive operand return must preserve its parent caller context"
13686        );
13687        assert_eq!(
13688            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13689            None,
13690            "the caller-overlap cache must preserve the loop-boundary return"
13691        );
13692    }
13693
13694    fn token_then_eof_atn() -> Atn {
13695        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13696            4, 1, 2, // version, parser, max token type
13697            3, // states
13698            2, 0, // rule start
13699            1, 0, // basic
13700            7, 0, // rule stop
13701            0, // non-greedy states
13702            0, // precedence states
13703            1, // rules
13704            0, // rule 0 start
13705            0, // modes
13706            0, // sets
13707            2, // transitions
13708            0, 1, 5, 1, 0, 0, // match token 1
13709            1, 2, 5, -1, 0, 0, // match EOF
13710            0, // decisions
13711        ]))
13712        .deserialize_parser()
13713        .expect("artificial parser ATN should deserialize")
13714    }
13715
13716    fn epsilon_cycle_atn() -> Atn {
13717        let mut atn = ParserAtnBuilder::new(1);
13718        for (state_number, kind) in [
13719            (0, AtnStateKind::RuleStart),
13720            (1, AtnStateKind::Basic),
13721            (2, AtnStateKind::RuleStop),
13722        ] {
13723            assert_eq!(
13724                atn.add_state(kind, Some(0)).expect("state").index(),
13725                state_number
13726            );
13727        }
13728        atn.set_rule_to_start_state(vec![0])
13729            .expect("rule start states");
13730        atn.set_rule_to_stop_state(vec![2])
13731            .expect("rule stop states");
13732        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13733            .expect("transition");
13734        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
13735            .expect("self-cycle transition");
13736        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13737            .expect("exit transition");
13738        finish_atn(atn)
13739    }
13740
13741    fn eof_then_action_atn() -> Atn {
13742        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13743            4, 1, 1, // version, parser, max token type
13744            3, // states
13745            2, 0, // rule start
13746            1, 0, // basic
13747            7, 0, // rule stop
13748            0, // non-greedy states
13749            0, // precedence states
13750            1, // rules
13751            0, // rule 0 start
13752            0, // modes
13753            0, // sets
13754            2, // transitions
13755            0, 1, 5, -1, 0, 0, // match EOF
13756            1, 2, 6, 0, 0, 0, // parser action
13757            0, // decisions
13758        ]))
13759        .deserialize_parser()
13760        .expect("artificial parser ATN should deserialize")
13761    }
13762
13763    fn noop_action_then_token_then_eof_atn() -> Atn {
13764        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13765            4, 1, 2, // version, parser, max token type
13766            4, // states
13767            2, 0, // rule start
13768            1, 0, // basic
13769            1, 0, // basic
13770            7, 0, // rule stop
13771            0, // non-greedy states
13772            0, // precedence states
13773            1, // rules
13774            0, // rule 0 start
13775            0, // modes
13776            0, // sets
13777            3, // transitions
13778            0, 1, 6, 0, -1, 0, // no-op parser action
13779            1, 2, 5, 1, 0, 0, // match token 1
13780            2, 3, 5, -1, 0, 0, // match EOF
13781            0, // decisions
13782        ]))
13783        .deserialize_parser()
13784        .expect("artificial no-op action ATN should deserialize")
13785    }
13786
13787    fn two_alt_decision_atn() -> Atn {
13788        let mut atn = ParserAtnBuilder::new(2);
13789        assert_eq!(
13790            atn.add_state(AtnStateKind::RuleStart, Some(0))
13791                .expect("state")
13792                .index(),
13793            0
13794        );
13795        assert_eq!(
13796            atn.add_state(AtnStateKind::BlockStart, Some(0))
13797                .expect("state")
13798                .index(),
13799            1
13800        );
13801        assert_eq!(
13802            atn.add_state(AtnStateKind::Basic, Some(0))
13803                .expect("state")
13804                .index(),
13805            2
13806        );
13807        assert_eq!(
13808            atn.add_state(AtnStateKind::Basic, Some(0))
13809                .expect("state")
13810                .index(),
13811            3
13812        );
13813        assert_eq!(
13814            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13815                .expect("state")
13816                .index(),
13817            4
13818        );
13819        assert_eq!(
13820            atn.add_state(AtnStateKind::RuleStop, Some(0))
13821                .expect("state")
13822                .index(),
13823            5
13824        );
13825        atn.set_rule_to_start_state(vec![0])
13826            .expect("rule start states");
13827        atn.set_rule_to_stop_state(vec![5])
13828            .expect("rule stop states");
13829        atn.add_decision_state(1).expect("decision state");
13830        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13831            .expect("transition");
13832        atn.add_transition(
13833            1,
13834            ParserTransitionSpec::Atom {
13835                target: 2,
13836                label: 1,
13837            },
13838        )
13839        .expect("transition");
13840        atn.add_transition(
13841            1,
13842            ParserTransitionSpec::Atom {
13843                target: 3,
13844                label: 2,
13845            },
13846        )
13847        .expect("transition");
13848        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
13849            .expect("transition");
13850        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13851            .expect("transition");
13852        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13853            .expect("transition");
13854        finish_atn(atn)
13855    }
13856
13857    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
13858    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
13859    fn optional_then_b_eof_atn() -> Atn {
13860        let mut atn = ParserAtnBuilder::new(3);
13861        assert_eq!(
13862            atn.add_state(AtnStateKind::RuleStart, Some(0))
13863                .expect("state")
13864                .index(),
13865            0
13866        );
13867        assert_eq!(
13868            atn.add_state(AtnStateKind::BlockStart, Some(0))
13869                .expect("state")
13870                .index(),
13871            1
13872        );
13873        assert_eq!(
13874            atn.add_state(AtnStateKind::Basic, Some(0))
13875                .expect("state")
13876                .index(),
13877            2
13878        );
13879        assert_eq!(
13880            atn.add_state(AtnStateKind::Basic, Some(0))
13881                .expect("state")
13882                .index(),
13883            3
13884        );
13885        assert_eq!(
13886            atn.add_state(AtnStateKind::Basic, Some(0))
13887                .expect("state")
13888                .index(),
13889            4
13890        );
13891        assert_eq!(
13892            atn.add_state(AtnStateKind::RuleStop, Some(0))
13893                .expect("state")
13894                .index(),
13895            5
13896        );
13897        atn.set_rule_to_start_state(vec![0])
13898            .expect("rule start states");
13899        atn.set_rule_to_stop_state(vec![5])
13900            .expect("rule stop states");
13901        atn.add_decision_state(1).expect("decision state");
13902        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13903            .expect("transition");
13904        // Optional block: match A then fall through, or skip straight to state 3.
13905        atn.add_transition(
13906            1,
13907            ParserTransitionSpec::Atom {
13908                target: 3,
13909                label: 1,
13910            },
13911        )
13912        .expect("transition");
13913        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
13914            .expect("transition");
13915        // Match B, then EOF.
13916        atn.add_transition(
13917            3,
13918            ParserTransitionSpec::Atom {
13919                target: 4,
13920                label: 2,
13921            },
13922        )
13923        .expect("transition");
13924        atn.add_transition(
13925            4,
13926            ParserTransitionSpec::Atom {
13927                target: 5,
13928                label: TOKEN_EOF,
13929            },
13930        )
13931        .expect("transition");
13932        finish_atn(atn)
13933    }
13934
13935    #[test]
13936    fn sync_decision_deletes_only_a_single_token() {
13937        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
13938        // expected. `(A)? B EOF` at the optional-block decision:
13939        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
13940        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
13941        //               without consuming and records the expected set for the
13942        //               subsequent mismatch (the parser must not over-consume both
13943        //               `C`s and accept the input).
13944        let atn = optional_then_b_eof_atn();
13945
13946        let mut single = mini_parser(vec![
13947            TestToken::new(3).with_text("c"),
13948            TestToken::new(2).with_text("b"),
13949            TestToken::eof("parser-test", 1, 2, 2),
13950        ]);
13951        single.rule_context_stack = vec![RuleContextFrame {
13952            rule_index: 0,
13953            invoking_state: 0,
13954        }];
13955        let children = single
13956            .sync_decision(&atn, 1, true, false)
13957            .expect("single extraneous token recovers");
13958        assert_eq!(children.len(), 1);
13959        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
13960        assert_eq!(single.number_of_syntax_errors(), 1);
13961        // Exactly one token consumed (the cursor now sits on `b`).
13962        assert_eq!(single.la(1), 2);
13963
13964        let mut double = mini_parser(vec![
13965            TestToken::new(3).with_text("c"),
13966            TestToken::new(3).with_text("c"),
13967            TestToken::new(2).with_text("b"),
13968            TestToken::eof("parser-test", 1, 3, 3),
13969        ]);
13970        double.rule_context_stack = vec![RuleContextFrame {
13971            rule_index: 0,
13972            invoking_state: 0,
13973        }];
13974        let result = double.sync_decision(&atn, 1, true, false);
13975        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
13976        // consume either `c`. It reports the mismatch at the first `c` (so the parser
13977        // does not over-consume both and accept the input). Nothing is consumed, so
13978        // the cursor still sits on the first `c` for rule-level recovery.
13979        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
13980        match error {
13981            AntlrError::ParserError { message, .. } => {
13982                assert!(message.starts_with("mismatched input"), "got: {message}");
13983            }
13984            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
13985        }
13986        assert_eq!(double.la(1), 3);
13987    }
13988
13989    /// The real serialized ATN that `antlr4-rust-gen` emits for
13990    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
13991    /// the loop is `EOF`. The loop decision is state 5.
13992    fn star_loop_then_eof_atn() -> Atn {
13993        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13994            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,
13995            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,
13996            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,
13997            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
13998        ]))
13999        .deserialize_parser()
14000        .expect("star-loop-then-EOF ATN should deserialize")
14001    }
14002
14003    /// ATN for `s : a+ Y ; a : X ;`.
14004    ///
14005    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
14006    /// `+` loop must not treat that zero-width child as a successful iteration
14007    /// and then re-enter the loop at the same token index.
14008    fn plus_loop_with_recovering_body_atn() -> Atn {
14009        let mut atn = ParserAtnBuilder::new(2);
14010        assert_eq!(
14011            atn.add_state(AtnStateKind::RuleStart, Some(0))
14012                .expect("state")
14013                .index(),
14014            0
14015        );
14016        assert_eq!(
14017            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
14018                .expect("state")
14019                .index(),
14020            1
14021        );
14022        assert_eq!(
14023            atn.add_state(AtnStateKind::Basic, Some(0))
14024                .expect("state")
14025                .index(),
14026            2
14027        );
14028        assert_eq!(
14029            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14030                .expect("state")
14031                .index(),
14032            3
14033        );
14034        assert_eq!(
14035            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
14036                .expect("state")
14037                .index(),
14038            4
14039        );
14040        assert_eq!(
14041            atn.add_state(AtnStateKind::LoopEnd, Some(0))
14042                .expect("state")
14043                .index(),
14044            5
14045        );
14046        assert_eq!(
14047            atn.add_state(AtnStateKind::RuleStop, Some(0))
14048                .expect("state")
14049                .index(),
14050            6
14051        );
14052        assert_eq!(
14053            atn.add_state(AtnStateKind::RuleStart, Some(1))
14054                .expect("state")
14055                .index(),
14056            7
14057        );
14058        assert_eq!(
14059            atn.add_state(AtnStateKind::Basic, Some(1))
14060                .expect("state")
14061                .index(),
14062            8
14063        );
14064        assert_eq!(
14065            atn.add_state(AtnStateKind::RuleStop, Some(1))
14066                .expect("state")
14067                .index(),
14068            9
14069        );
14070        atn.set_rule_to_start_state(vec![0, 7])
14071            .expect("rule start states");
14072        atn.set_rule_to_stop_state(vec![6, 9])
14073            .expect("rule stop states");
14074        atn.set_end_state(1, 3).expect("block end state");
14075        atn.set_loop_back_state(5, 4).expect("loop back state");
14076        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14077            .expect("transition");
14078        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14079            .expect("transition");
14080        atn.add_transition(
14081            2,
14082            ParserTransitionSpec::Rule {
14083                target: 7,
14084                rule_index: 1,
14085                follow_state: 3,
14086                precedence: 0,
14087            },
14088        )
14089        .expect("transition");
14090        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14091            .expect("transition");
14092        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
14093            .expect("transition");
14094        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14095            .expect("transition");
14096        atn.add_transition(
14097            5,
14098            ParserTransitionSpec::Atom {
14099                target: 6,
14100                label: 2,
14101            },
14102        )
14103        .expect("transition");
14104        atn.add_transition(
14105            7,
14106            ParserTransitionSpec::Atom {
14107                target: 8,
14108                label: 1,
14109            },
14110        )
14111        .expect("transition");
14112        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14113            .expect("transition");
14114        finish_atn(atn)
14115    }
14116
14117    #[test]
14118    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
14119        let atn = plus_loop_with_recovering_body_atn();
14120        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14121
14122        let error = parser
14123            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
14124            .expect_err("EOF recovery should report a bounded mismatch");
14125
14126        let AntlrError::ParserError { message, .. } = error else {
14127            panic!("expected ParserError, got {error:?}");
14128        };
14129        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
14130        assert_eq!(parser.number_of_syntax_errors(), 1);
14131        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
14132    }
14133
14134    #[test]
14135    fn sync_decision_deletes_token_before_eof_at_loop_back() {
14136        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
14137        // At the loop ENTRY (loop_back = false) a single unexpected token before
14138        // EOF is deleted as an error node (then the generated EOF match consumes
14139        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
14140        // EOF must be a valid scan-stop for this to fire.
14141        let atn = star_loop_then_eof_atn();
14142        let mut parser = mini_parser(vec![
14143            TestToken::new(2).with_text("c"),
14144            TestToken::eof("parser-test", 1, 1, 1),
14145        ]);
14146        parser.rule_context_stack = vec![RuleContextFrame {
14147            rule_index: 0,
14148            invoking_state: 0,
14149        }];
14150        let children = parser
14151            .sync_decision(&atn, 5, true, false)
14152            .expect("single token before EOF recovers");
14153        assert_eq!(children.len(), 1);
14154        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
14155        assert_eq!(parser.number_of_syntax_errors(), 1);
14156        assert_eq!(
14157            parser.la(1),
14158            TOKEN_EOF,
14159            "EOF is left for the rule's EOF match"
14160        );
14161    }
14162
14163    #[test]
14164    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
14165        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
14166        // single-token deletion, which fails because LA(2) = `c` is not expected —
14167        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
14168        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
14169        let atn = star_loop_then_eof_atn();
14170        let mut parser = mini_parser(vec![
14171            TestToken::new(2).with_text("c"),
14172            TestToken::new(2).with_text("c"),
14173            TestToken::eof("parser-test", 1, 2, 2),
14174        ]);
14175        parser.rule_context_stack = vec![RuleContextFrame {
14176            rule_index: 0,
14177            invoking_state: 0,
14178        }];
14179        let error = parser
14180            .sync_decision(&atn, 5, true, false)
14181            .expect_err("two tokens at the loop entry must not be deleted");
14182        match error {
14183            AntlrError::ParserError { message, .. } => {
14184                assert!(message.starts_with("mismatched input"), "got: {message}");
14185            }
14186            other => panic!("expected mismatched-input ParserError, got {other:?}"),
14187        }
14188        assert_eq!(
14189            parser.la(1),
14190            2,
14191            "nothing consumed; cursor still on first `c`"
14192        );
14193    }
14194
14195    #[test]
14196    fn sync_decision_consumes_until_eof_at_loop_back() {
14197        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
14198        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
14199        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
14200        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
14201        // post-`a` state directly: `c c <EOF>` with loop_back = true.
14202        let atn = star_loop_then_eof_atn();
14203        let mut parser = mini_parser(vec![
14204            TestToken::new(2).with_text("c"),
14205            TestToken::new(2).with_text("c"),
14206            TestToken::eof("parser-test", 1, 2, 2),
14207        ]);
14208        parser.rule_context_stack = vec![RuleContextFrame {
14209            rule_index: 0,
14210            invoking_state: 0,
14211        }];
14212        let children = parser
14213            .sync_decision(&atn, 5, false, true)
14214            .expect("loop-back multi-token deletion recovers onto EOF");
14215        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
14216        assert!(
14217            children
14218                .iter()
14219                .all(|child| parser.node(*child).kind() == NodeKind::Error)
14220        );
14221        assert_eq!(parser.number_of_syntax_errors(), 1);
14222        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
14223    }
14224
14225    fn predicate_after_token_atn() -> Atn {
14226        let mut atn = ParserAtnBuilder::new(2);
14227        assert_eq!(
14228            atn.add_state(AtnStateKind::RuleStart, Some(0))
14229                .expect("state")
14230                .index(),
14231            0
14232        );
14233        assert_eq!(
14234            atn.add_state(AtnStateKind::Basic, Some(0))
14235                .expect("state")
14236                .index(),
14237            1
14238        );
14239        assert_eq!(
14240            atn.add_state(AtnStateKind::Basic, Some(0))
14241                .expect("state")
14242                .index(),
14243            2
14244        );
14245        assert_eq!(
14246            atn.add_state(AtnStateKind::Basic, Some(0))
14247                .expect("state")
14248                .index(),
14249            3
14250        );
14251        assert_eq!(
14252            atn.add_state(AtnStateKind::RuleStop, Some(0))
14253                .expect("state")
14254                .index(),
14255            4
14256        );
14257        atn.set_rule_to_start_state(vec![0])
14258            .expect("rule start states");
14259        atn.set_rule_to_stop_state(vec![4])
14260            .expect("rule stop states");
14261        atn.add_transition(
14262            0,
14263            ParserTransitionSpec::Atom {
14264                target: 1,
14265                label: 1,
14266            },
14267        )
14268        .expect("transition");
14269        atn.add_transition(
14270            1,
14271            ParserTransitionSpec::Predicate {
14272                target: 2,
14273                rule_index: 0,
14274                pred_index: 0,
14275                context_dependent: false,
14276            },
14277        )
14278        .expect("transition");
14279        atn.add_transition(
14280            2,
14281            ParserTransitionSpec::Atom {
14282                target: 3,
14283                label: 2,
14284            },
14285        )
14286        .expect("transition");
14287        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14288            .expect("transition");
14289        finish_atn(atn)
14290    }
14291
14292    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
14293        let mut atn = ParserAtnBuilder::new(1);
14294        for (state_number, kind) in [
14295            (0, AtnStateKind::RuleStart),
14296            (1, AtnStateKind::BlockStart),
14297            (2, AtnStateKind::Basic),
14298            (3, AtnStateKind::Basic),
14299            (4, AtnStateKind::Basic),
14300            (5, AtnStateKind::Basic),
14301            (6, AtnStateKind::BlockEnd),
14302            (7, AtnStateKind::RuleStop),
14303        ] {
14304            assert_eq!(
14305                atn.add_state(kind, Some(0)).expect("state").index(),
14306                state_number
14307            );
14308        }
14309        atn.set_rule_to_start_state(vec![0])
14310            .expect("rule start states");
14311        atn.set_rule_to_stop_state(vec![7])
14312            .expect("rule stop states");
14313        atn.add_decision_state(1).expect("decision state");
14314        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14315            .expect("transition");
14316        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14317            .expect("transition");
14318        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14319            .expect("transition");
14320        atn.add_transition(
14321            2,
14322            ParserTransitionSpec::Predicate {
14323                target: 4,
14324                rule_index: 0,
14325                pred_index: pred_indexes[0],
14326                context_dependent: false,
14327            },
14328        )
14329        .expect("transition");
14330        atn.add_transition(
14331            3,
14332            ParserTransitionSpec::Predicate {
14333                target: 5,
14334                rule_index: 0,
14335                pred_index: pred_indexes[1],
14336                context_dependent: false,
14337            },
14338        )
14339        .expect("transition");
14340        atn.add_transition(
14341            4,
14342            ParserTransitionSpec::Atom {
14343                target: 6,
14344                label: 1,
14345            },
14346        )
14347        .expect("transition");
14348        atn.add_transition(
14349            5,
14350            ParserTransitionSpec::Atom {
14351                target: 6,
14352                label: 1,
14353            },
14354        )
14355        .expect("transition");
14356        atn.add_transition(
14357            6,
14358            ParserTransitionSpec::Atom {
14359                target: 7,
14360                label: TOKEN_EOF,
14361            },
14362        )
14363        .expect("transition");
14364        finish_atn(atn)
14365    }
14366
14367    fn nested_nullable_context_atn() -> Atn {
14368        let mut atn = ParserAtnBuilder::new(1);
14369        for state_number in 0..=20 {
14370            let kind = match state_number {
14371                0 | 10 | 16 => AtnStateKind::RuleStart,
14372                9 | 15 | 20 => AtnStateKind::RuleStop,
14373                _ => AtnStateKind::Basic,
14374            };
14375            let rule_index = match state_number {
14376                0..=9 => 0,
14377                10..=15 => 1,
14378                _ => 2,
14379            };
14380            assert_eq!(
14381                atn.add_state(kind, Some(rule_index))
14382                    .expect("state")
14383                    .index(),
14384                state_number
14385            );
14386        }
14387        atn.set_rule_to_start_state(vec![0, 10, 16])
14388            .expect("rule start states");
14389        atn.set_rule_to_stop_state(vec![9, 15, 20])
14390            .expect("rule stop states");
14391        atn.add_transition(
14392            1,
14393            ParserTransitionSpec::Rule {
14394                target: 10,
14395                rule_index: 1,
14396                follow_state: 8,
14397                precedence: 0,
14398            },
14399        )
14400        .expect("transition");
14401        atn.add_transition(
14402            8,
14403            ParserTransitionSpec::Atom {
14404                target: 9,
14405                label: 1,
14406            },
14407        )
14408        .expect("transition");
14409        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14410            .expect("transition");
14411        atn.add_transition(
14412            2,
14413            ParserTransitionSpec::Rule {
14414                target: 16,
14415                rule_index: 2,
14416                follow_state: 14,
14417                precedence: 0,
14418            },
14419        )
14420        .expect("transition");
14421        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
14422            .expect("transition");
14423        finish_atn(atn)
14424    }
14425
14426    fn generated_match_recovery_atn() -> Atn {
14427        let mut atn = ParserAtnBuilder::new(2);
14428        assert_eq!(
14429            atn.add_state(AtnStateKind::RuleStart, Some(0))
14430                .expect("state")
14431                .index(),
14432            0
14433        );
14434        assert_eq!(
14435            atn.add_state(AtnStateKind::Basic, Some(0))
14436                .expect("state")
14437                .index(),
14438            1
14439        );
14440        assert_eq!(
14441            atn.add_state(AtnStateKind::Basic, Some(0))
14442                .expect("state")
14443                .index(),
14444            2
14445        );
14446        assert_eq!(
14447            atn.add_state(AtnStateKind::RuleStop, Some(0))
14448                .expect("state")
14449                .index(),
14450            3
14451        );
14452        assert_eq!(
14453            atn.add_state(AtnStateKind::RuleStart, Some(1))
14454                .expect("state")
14455                .index(),
14456            4
14457        );
14458        assert_eq!(
14459            atn.add_state(AtnStateKind::RuleStop, Some(1))
14460                .expect("state")
14461                .index(),
14462            5
14463        );
14464        atn.set_rule_to_start_state(vec![0, 4])
14465            .expect("rule start states");
14466        atn.set_rule_to_stop_state(vec![3, 5])
14467            .expect("rule stop states");
14468        atn.add_transition(
14469            1,
14470            ParserTransitionSpec::Rule {
14471                target: 4,
14472                rule_index: 1,
14473                follow_state: 2,
14474                precedence: 0,
14475            },
14476        )
14477        .expect("transition");
14478        atn.add_transition(
14479            2,
14480            ParserTransitionSpec::Atom {
14481                target: 3,
14482                label: TOKEN_EOF,
14483            },
14484        )
14485        .expect("transition");
14486        finish_atn(atn)
14487    }
14488
14489    fn complement_set_atn() -> Atn {
14490        let mut atn = ParserAtnBuilder::new(1);
14491        assert_eq!(
14492            atn.add_state(AtnStateKind::RuleStart, Some(0))
14493                .expect("state")
14494                .index(),
14495            0
14496        );
14497        assert_eq!(
14498            atn.add_state(AtnStateKind::RuleStop, Some(0))
14499                .expect("state")
14500                .index(),
14501            1
14502        );
14503        atn.set_rule_to_start_state(vec![0])
14504            .expect("rule start states");
14505        atn.set_rule_to_stop_state(vec![1])
14506            .expect("rule stop states");
14507        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
14508        atn.add_transition(
14509            0,
14510            ParserTransitionSpec::NotSet {
14511                target: 1,
14512                set: excluded,
14513            },
14514        )
14515        .expect("transition");
14516        finish_atn(atn)
14517    }
14518
14519    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
14520    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
14521    fn wildcard_then_eof_atn() -> Atn {
14522        let mut atn = ParserAtnBuilder::new(1);
14523        assert_eq!(
14524            atn.add_state(AtnStateKind::RuleStart, Some(0))
14525                .expect("state")
14526                .index(),
14527            0
14528        );
14529        assert_eq!(
14530            atn.add_state(AtnStateKind::RuleStop, Some(0))
14531                .expect("state")
14532                .index(),
14533            1
14534        );
14535        assert_eq!(
14536            atn.add_state(AtnStateKind::Basic, Some(0))
14537                .expect("state")
14538                .index(),
14539            2
14540        );
14541        atn.set_rule_to_start_state(vec![0])
14542            .expect("rule start states");
14543        atn.set_rule_to_stop_state(vec![1])
14544            .expect("rule stop states");
14545        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
14546            .expect("transition");
14547        atn.add_transition(
14548            2,
14549            ParserTransitionSpec::Atom {
14550                target: 1,
14551                label: TOKEN_EOF,
14552            },
14553        )
14554        .expect("transition");
14555        finish_atn(atn)
14556    }
14557
14558    #[test]
14559    fn parser_matches_token_and_reports_mismatch() {
14560        let source = Source {
14561            tokens: vec![
14562                TestToken::new(1).with_text("x"),
14563                TestToken::eof("parser-test", 1, 1, 1),
14564            ],
14565            index: 0,
14566        };
14567        let data = RecognizerData::new(
14568            "Mini.g4",
14569            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14570        );
14571        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
14572        let matched = parser.match_token(1).expect("token 1 should match");
14573        assert_eq!(parser.node(matched).text(), "x");
14574        assert!(parser.match_token(1).is_err());
14575    }
14576
14577    #[test]
14578    fn parser_matches_token_sets() {
14579        let mut parser = mini_parser(vec![
14580            TestToken::new(1).with_text("x"),
14581            TestToken::eof("parser-test", 1, 1, 1),
14582        ]);
14583
14584        let matched = parser
14585            .match_set(&[(1, 1), (3, 4)])
14586            .expect("token set should match");
14587        assert_eq!(parser.node(matched).text(), "x");
14588        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
14589    }
14590
14591    #[test]
14592    fn generated_rule_api_tracks_state_and_precedence() {
14593        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14594
14595        let context = parser.enter_rule(7, 2);
14596        assert_eq!(context.rule_index(), 2);
14597        assert_eq!(parser.state(), 7);
14598        assert_eq!(
14599            parser.rule_context_stack,
14600            vec![RuleContextFrame {
14601                rule_index: 2,
14602                invoking_state: 7
14603            }]
14604        );
14605
14606        let recursive = parser.enter_recursion_rule(11, 3, 4);
14607        assert_eq!(recursive.rule_index(), 3);
14608        assert!(parser.precpred(4));
14609        assert!(parser.precpred(5));
14610        assert!(!parser.precpred(3));
14611
14612        let next = parser.push_new_recursion_context(13, 3);
14613        assert_eq!(next.invoking_state(), 13);
14614        parser.unroll_recursion_context();
14615        assert_eq!(parser.precedence_stack, vec![0]);
14616        assert_eq!(
14617            parser.rule_context_stack,
14618            vec![RuleContextFrame {
14619                rule_index: 2,
14620                invoking_state: 7
14621            }]
14622        );
14623
14624        parser.exit_rule();
14625        assert!(parser.rule_context_stack.is_empty());
14626    }
14627
14628    #[test]
14629    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
14630        let mut parser = mini_parser(vec![
14631            TestToken::new(1).with_text("x"),
14632            TestToken::eof("parser-test", 1, 1, 1),
14633        ]);
14634        let matched = parser.match_token(1).expect("token should match");
14635        assert_eq!(parser.node(matched).text(), "x");
14636        parser.record_generated_syntax_error();
14637        parser.set_int_member(7, 11);
14638        parser.set_build_parse_trees(false);
14639        parser.set_report_diagnostic_errors(true);
14640        parser.set_prediction_mode(PredictionMode::Sll);
14641        parser.set_bail_on_error(true);
14642        let _context = parser.enter_recursion_rule(9, 0, 4);
14643        parser.pending_invoking_states.push(5);
14644        parser.unknown_predicate_hits.push((0, 1));
14645        parser.unhandled_action_hits.push((0, 2));
14646
14647        parser.reset();
14648
14649        assert_eq!(parser.input.index(), 0);
14650        assert_eq!(parser.la(1), 1);
14651        assert_eq!(parser.state(), -1);
14652        assert_eq!(parser.number_of_syntax_errors(), 0);
14653        assert_eq!(parser.parse_tree_storage().node_count(), 0);
14654        assert!(parser.rule_context_stack.is_empty());
14655        assert!(parser.pending_invoking_states.is_empty());
14656        assert_eq!(parser.precedence_stack, [0]);
14657        assert!(parser.unknown_predicate_hits.is_empty());
14658        assert!(parser.unhandled_action_hits.is_empty());
14659        assert_eq!(parser.int_member(7), Some(11));
14660        assert!(!parser.build_parse_trees());
14661        assert!(parser.report_diagnostic_errors());
14662        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
14663        assert!(parser.bail_on_error());
14664    }
14665
14666    #[test]
14667    fn set_token_stream_replaces_input_and_resets_parser() {
14668        let mut parser = mini_parser(vec![
14669            TestToken::new(1).with_text("old"),
14670            TestToken::eof("parser-test", 1, 1, 1),
14671        ]);
14672        parser.consume();
14673        parser.record_generated_syntax_error();
14674        let replacement = CommonTokenStream::new(Source {
14675            tokens: vec![
14676                TestToken::new(2).with_text("new"),
14677                TestToken::eof("parser-test", 1, 1, 1),
14678            ],
14679            index: 0,
14680        });
14681
14682        parser.set_token_stream(replacement);
14683
14684        assert_eq!(parser.input.index(), 0);
14685        assert_eq!(parser.la(1), 2);
14686        assert_eq!(parser.input.text_all(), "new");
14687        assert_eq!(parser.number_of_syntax_errors(), 0);
14688    }
14689
14690    #[test]
14691    fn active_invocation_states_exclude_the_root_frame() {
14692        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14693
14694        let _root = parser.enter_rule(0, 0);
14695        assert!(parser.active_invocation_states().is_empty());
14696
14697        let marker = parser.push_invoking_state(6);
14698        let _child = parser.enter_rule(2, 1);
14699        parser.discard_invoking_state(marker);
14700        assert_eq!(parser.active_invocation_states(), [6]);
14701
14702        let marker = parser.push_invoking_state(13);
14703        let _grandchild = parser.enter_rule(4, 2);
14704        parser.discard_invoking_state(marker);
14705        assert_eq!(parser.active_invocation_states(), [13, 6]);
14706
14707        parser.exit_rule();
14708        parser.exit_rule();
14709        parser.exit_rule();
14710    }
14711
14712    #[test]
14713    fn parser_predicates_support_token_adjacency() {
14714        let mut parser = mini_parser(vec![
14715            TestToken::new(1).with_text("=").with_span(0, 0),
14716            TestToken::new(1).with_text(">").with_span(1, 1),
14717            TestToken::eof("parser-test", 2, 1, 2),
14718        ]);
14719        parser.consume();
14720        parser.consume();
14721
14722        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
14723
14724        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14725
14726        let mut parser = mini_parser(vec![
14727            TestToken::new(1).with_text("=").with_span(0, 0),
14728            TestToken::new(1)
14729                .with_text(" ")
14730                .with_channel(HIDDEN_CHANNEL)
14731                .with_span(1, 1),
14732            TestToken::new(1).with_text(">").with_span(2, 2),
14733            TestToken::eof("parser-test", 3, 1, 3),
14734        ]);
14735        parser.consume();
14736        parser.consume();
14737
14738        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14739    }
14740
14741    #[test]
14742    fn parser_predicates_support_context_child_text_checks() {
14743        let mut parser = mini_parser(vec![
14744            TestToken::new(1).with_text("var"),
14745            TestToken::eof("parser-test", 1, 1, 1),
14746        ]);
14747        let mut context = ParserRuleContext::new(1, 0);
14748        let mut child_context = ParserRuleContext::new(2, 0);
14749        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
14750        parser.tree.add_child(&mut child_context, terminal);
14751        let child = parser.rule_node(child_context);
14752        parser.tree.add_child(&mut context, child);
14753        let predicates = [(
14754            1,
14755            0,
14756            ParserPredicate::ContextChildRuleTextNotEquals {
14757                rule_index: 2,
14758                text: "var",
14759            },
14760        )];
14761
14762        assert!(
14763            !parser.parser_semantic_predicate_matches_with_context_and_local(
14764                &predicates,
14765                1,
14766                0,
14767                &context,
14768                0,
14769            )
14770        );
14771    }
14772
14773    #[test]
14774    fn context_expected_symbols_walks_nullable_parent_contexts() {
14775        let atn = nested_nullable_context_atn();
14776        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14777        parser.rule_context_stack = vec![
14778            RuleContextFrame {
14779                rule_index: 0,
14780                invoking_state: 0,
14781            },
14782            RuleContextFrame {
14783                rule_index: 1,
14784                invoking_state: 1,
14785            },
14786            RuleContextFrame {
14787                rule_index: 2,
14788                invoking_state: 2,
14789            },
14790        ];
14791
14792        let expected = parser.context_expected_symbols(&atn);
14793
14794        assert!(expected.contains(&1));
14795        assert!(expected.contains(&TOKEN_EOF));
14796    }
14797
14798    #[test]
14799    fn prediction_context_return_states_track_rule_stack_changes() {
14800        let atn = nested_nullable_context_atn();
14801        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14802        parser.rule_context_stack = vec![
14803            RuleContextFrame {
14804                rule_index: 0,
14805                invoking_state: 0,
14806            },
14807            RuleContextFrame {
14808                rule_index: 1,
14809                invoking_state: 1,
14810            },
14811            RuleContextFrame {
14812                rule_index: 2,
14813                invoking_state: 2,
14814            },
14815        ];
14816
14817        let initial_version = parser.rule_context_version();
14818        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14819        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14820        assert_eq!(first, second);
14821        assert_eq!(parser.rule_context_version(), initial_version);
14822
14823        parser.exit_rule();
14824        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14825        assert_ne!(first, after_pop);
14826        assert_ne!(parser.rule_context_version(), initial_version);
14827    }
14828
14829    #[test]
14830    fn generated_match_token_recovers_missing_token_from_context_follow() {
14831        let atn = generated_match_recovery_atn();
14832        let data = RecognizerData::new(
14833            "Mini.g4",
14834            Vocabulary::new(
14835                [None, Some("'X'"), Some("'Y'")],
14836                [None, Some("X"), Some("Y")],
14837                [None::<&str>, None, None],
14838            ),
14839        );
14840        let mut parser = BaseParser::new(
14841            CommonTokenStream::new(Source {
14842                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14843                index: 0,
14844            }),
14845            data,
14846        );
14847        parser.rule_context_stack = vec![
14848            RuleContextFrame {
14849                rule_index: 0,
14850                invoking_state: 0,
14851            },
14852            RuleContextFrame {
14853                rule_index: 1,
14854                invoking_state: 1,
14855            },
14856        ];
14857        assert_eq!(parser.number_of_syntax_errors(), 0);
14858
14859        let node = parser
14860            .match_token_recovering(2, 5, &atn)
14861            .expect("generated match should insert missing token");
14862
14863        assert_eq!(node.children().len(), 1);
14864        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
14865        assert_eq!(
14866            node.clone()
14867                .into_child_iter()
14868                .map(|child| parser.node(child).text())
14869                .collect::<Vec<_>>(),
14870            ["<missing 'Y'>"]
14871        );
14872        // Single-token insertion synthesizes a missing token and consumes nothing,
14873        // so no EOF terminal is consumed even though lookahead is EOF.
14874        assert!(!node.consumed_eof());
14875        assert_eq!(parser.la(1), TOKEN_EOF);
14876        assert_eq!(parser.number_of_syntax_errors(), 1);
14877        assert_eq!(
14878            parser.generated_parser_diagnostics,
14879            [ParserDiagnostic {
14880                line: 1,
14881                column: 3,
14882                message: "missing 'Y' at '<EOF>'".to_owned(),
14883            }]
14884        );
14885    }
14886
14887    #[test]
14888    fn generated_match_token_counts_single_token_deletion_recovery() {
14889        let atn = generated_match_recovery_atn();
14890        let data = RecognizerData::new(
14891            "Mini.g4",
14892            Vocabulary::new(
14893                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
14894                [None, Some("X"), Some("Y"), Some("Z")],
14895                [None::<&str>, None, None, None],
14896            ),
14897        );
14898        let mut parser = BaseParser::new(
14899            CommonTokenStream::new(Source {
14900                tokens: vec![
14901                    TestToken::new(3).with_text("z"),
14902                    TestToken::new(2).with_text("y"),
14903                    TestToken::eof("parser-test", 3, 1, 3),
14904                ],
14905                index: 0,
14906            }),
14907            data,
14908        );
14909
14910        let node = parser
14911            .match_token_recovering(2, 5, &atn)
14912            .expect("generated match should delete the extraneous token");
14913
14914        assert_eq!(node.children().len(), 2);
14915        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
14916        assert_eq!(parser.node(node.children()[0]).text(), "z");
14917        assert_eq!(parser.node(node.children()[1]).text(), "y");
14918        assert_eq!(
14919            node.into_child_iter()
14920                .map(|child| parser.node(child).text())
14921                .collect::<Vec<_>>(),
14922            ["z", "y"]
14923        );
14924        assert_eq!(parser.number_of_syntax_errors(), 1);
14925    }
14926
14927    #[test]
14928    fn generated_match_token_iterates_single_success_without_a_children_vec() {
14929        let atn = generated_match_recovery_atn();
14930        let data = RecognizerData::new(
14931            "Mini.g4",
14932            Vocabulary::new(
14933                [None, Some("'X'"), Some("'Y'")],
14934                [None, Some("X"), Some("Y")],
14935                [None::<&str>, None, None],
14936            ),
14937        );
14938        let mut parser = BaseParser::new(
14939            CommonTokenStream::new(Source {
14940                tokens: vec![
14941                    TestToken::new(2).with_text("y"),
14942                    TestToken::eof("parser-test", 1, 1, 1),
14943                ],
14944                index: 0,
14945            }),
14946            data,
14947        );
14948
14949        let node = parser
14950            .match_token_recovering(2, 5, &atn)
14951            .expect("generated match should consume the expected token");
14952
14953        assert_eq!(
14954            node.into_child_iter()
14955                .map(|child| parser.node(child).text())
14956                .collect::<Vec<_>>(),
14957            ["y"]
14958        );
14959        assert_eq!(parser.number_of_syntax_errors(), 0);
14960    }
14961
14962    #[test]
14963    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
14964        let atn = generated_match_recovery_atn();
14965        let data = RecognizerData::new(
14966            "Mini.g4",
14967            Vocabulary::new(
14968                [None, Some("'X'"), Some("'Y'")],
14969                [None, Some("X"), Some("Y")],
14970                [None::<&str>, None, None],
14971            ),
14972        );
14973        let mut parser = BaseParser::new(
14974            CommonTokenStream::new(Source {
14975                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14976                index: 0,
14977            }),
14978            data,
14979        );
14980        parser.rule_context_stack = vec![
14981            RuleContextFrame {
14982                rule_index: 0,
14983                invoking_state: 0,
14984            },
14985            RuleContextFrame {
14986                rule_index: 1,
14987                invoking_state: 1,
14988            },
14989        ];
14990        let marker = parser.generated_diagnostics_checkpoint();
14991
14992        let _ = parser
14993            .match_token_recovering(2, 5, &atn)
14994            .expect("generated match should insert missing token");
14995        assert_eq!(parser.number_of_syntax_errors(), 1);
14996
14997        parser.restore_generated_diagnostics(marker);
14998
14999        assert_eq!(parser.number_of_syntax_errors(), 0);
15000        assert!(parser.generated_parser_diagnostics.is_empty());
15001    }
15002
15003    #[test]
15004    fn generated_prediction_diagnostics_use_adaptive_context() {
15005        let atn = two_alt_decision_atn();
15006        let data = RecognizerData::new(
15007            "Mini.g4",
15008            Vocabulary::new(
15009                [None, Some("'x'"), Some("'y'")],
15010                [None, Some("X"), Some("Y")],
15011                [None::<&str>, None, None],
15012            ),
15013        )
15014        .with_rule_names(["s"]);
15015        let mut parser = BaseParser::new(
15016            CommonTokenStream::new(Source {
15017                tokens: vec![
15018                    TestToken::new(1)
15019                        .with_text("x")
15020                        .with_position(1, 0)
15021                        .with_span(0, 0),
15022                    TestToken::new(2)
15023                        .with_text("y")
15024                        .with_position(1, 2)
15025                        .with_span(1, 1),
15026                    TestToken::eof("parser-test", 2, 1, 3),
15027                ],
15028                index: 0,
15029            }),
15030            data,
15031        );
15032        parser.set_report_diagnostic_errors(true);
15033
15034        parser.record_generated_prediction_diagnostic(
15035            &atn,
15036            1,
15037            &ParserAtnPrediction {
15038                alt: 1,
15039                requires_full_context: true,
15040                has_semantic_context: false,
15041                diagnostic: Some(ParserAtnPredictionDiagnostic {
15042                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
15043                    start_index: 0,
15044                    sll_stop_index: 1,
15045                    ll_stop_index: 0,
15046                    conflicting_alts: vec![1, 2],
15047                    exact: false,
15048                }),
15049            },
15050        );
15051        // Ambiguities from the default LL prediction mode are non-exact, so —
15052        // matching Java's exactOnly DiagnosticErrorListener — only the
15053        // attempting-full-context line is reported. Exact-ambiguity mode
15054        // reports the ambiguity itself.
15055        parser.record_generated_prediction_diagnostic(
15056            &atn,
15057            1,
15058            &ParserAtnPrediction {
15059                alt: 1,
15060                requires_full_context: true,
15061                has_semantic_context: false,
15062                diagnostic: Some(ParserAtnPredictionDiagnostic {
15063                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
15064                    start_index: 0,
15065                    sll_stop_index: 1,
15066                    ll_stop_index: 1,
15067                    conflicting_alts: vec![1, 2],
15068                    exact: false,
15069                }),
15070            },
15071        );
15072
15073        assert_eq!(
15074            parser.generated_parser_diagnostics,
15075            [
15076                ParserDiagnostic {
15077                    line: 1,
15078                    column: 2,
15079                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
15080                },
15081                ParserDiagnostic {
15082                    line: 1,
15083                    column: 0,
15084                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
15085                },
15086                ParserDiagnostic {
15087                    line: 1,
15088                    column: 2,
15089                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
15090                },
15091            ]
15092        );
15093    }
15094
15095    #[test]
15096    fn generated_match_not_set_recovers_empty_complement_at_eof() {
15097        let atn = complement_set_atn();
15098        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15099        parser.rule_context_stack = vec![RuleContextFrame {
15100            rule_index: 0,
15101            invoking_state: 0,
15102        }];
15103
15104        let node = parser
15105            .match_not_token_set_recovering(
15106                atn.token_set(0).expect("excluded token set"),
15107                1,
15108                1,
15109                1,
15110                &atn,
15111            )
15112            .expect("empty complement should recover at EOF");
15113
15114        assert_eq!(node.children().len(), 1);
15115        // Recovery synthesizes a missing token without consuming EOF, so the
15116        // enclosing rule must not record EOF as its stop token.
15117        assert!(!node.consumed_eof());
15118        assert_eq!(parser.la(1), TOKEN_EOF);
15119        assert_eq!(
15120            parser.generated_parser_diagnostics,
15121            [ParserDiagnostic {
15122                line: 1,
15123                column: 1,
15124                message: "missing {} at '<EOF>'".to_owned(),
15125            }]
15126        );
15127    }
15128
15129    #[test]
15130    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
15131        // `start : . EOF ;` on empty input. The wildcard is modeled as an
15132        // empty-complement not-set; at EOF the follow state (the explicit EOF
15133        // match) expects EOF, so even in the start rule recovery must perform
15134        // single-token insertion (`<missing ...>`) rather than aborting — matching
15135        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
15136        let atn = wildcard_then_eof_atn();
15137        let data = RecognizerData::new(
15138            "Mini.g4",
15139            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15140        );
15141        let mut parser = BaseParser::new(
15142            CommonTokenStream::new(Source {
15143                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
15144                index: 0,
15145            }),
15146            data,
15147        );
15148        parser.rule_context_stack = vec![RuleContextFrame {
15149            rule_index: 0,
15150            invoking_state: 0,
15151        }];
15152
15153        let node = parser
15154            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
15155            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
15156
15157        // A single `<missing ...>` error node is inserted; EOF is not consumed.
15158        assert_eq!(node.children().len(), 1);
15159        assert!(!node.consumed_eof());
15160        assert!(
15161            parser
15162                .node(node.children()[0])
15163                .text()
15164                .starts_with("<missing")
15165        );
15166        assert_eq!(parser.la(1), TOKEN_EOF);
15167        assert_eq!(
15168            parser.generated_parser_diagnostics,
15169            [ParserDiagnostic {
15170                line: 1,
15171                column: 1,
15172                message: "missing 'x' at '<EOF>'".to_owned(),
15173            }]
15174        );
15175    }
15176
15177    #[test]
15178    fn generated_rule_recovery_consumes_to_parent_follow() {
15179        let atn = generated_match_recovery_atn();
15180        let data = RecognizerData::new(
15181            "Mini.g4",
15182            Vocabulary::new(
15183                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15184                [None, Some("X"), Some("Y"), Some("Z")],
15185                [None::<&str>, None, None, None],
15186            ),
15187        );
15188        let mut parser = BaseParser::new(
15189            CommonTokenStream::new(Source {
15190                tokens: vec![
15191                    TestToken::new(3).with_text("z"),
15192                    TestToken::eof("parser-test", 1, 1, 1),
15193                ],
15194                index: 0,
15195            }),
15196            data,
15197        );
15198        let _parent = parser.enter_rule(0, 0);
15199        let marker = parser.push_invoking_state(1);
15200        let mut child = parser.enter_rule(4, 1);
15201        parser.discard_invoking_state(marker);
15202
15203        parser.recover_generated_rule(
15204            &mut child,
15205            &atn,
15206            AntlrError::ParserError {
15207                line: 1,
15208                column: 0,
15209                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15210            },
15211        );
15212        let tree = parser.finish_rule(child, false);
15213
15214        assert_eq!(parser.la(1), TOKEN_EOF);
15215        assert_eq!(
15216            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
15217            "(a z)"
15218        );
15219        assert_eq!(parser.number_of_syntax_errors(), 1);
15220        assert_eq!(
15221            parser.generated_parser_diagnostics,
15222            [ParserDiagnostic {
15223                line: 1,
15224                column: 0,
15225                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15226            }]
15227        );
15228        parser.exit_rule();
15229    }
15230
15231    #[test]
15232    fn greedy_ll1_alt_handles_nullable_loop_exit() {
15233        let mut body_symbols = TokenBitSet::default();
15234        body_symbols.insert(1);
15235        let entry = DecisionLookahead {
15236            transitions: vec![
15237                TransitionLookSet {
15238                    symbols: body_symbols,
15239                    nullable: false,
15240                },
15241                TransitionLookSet {
15242                    symbols: TokenBitSet::default(),
15243                    nullable: true,
15244                },
15245            ],
15246        };
15247
15248        assert_eq!(ll1_unique_alt(&entry, 2), None);
15249        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
15250        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
15251        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
15252    }
15253
15254    #[test]
15255    fn ordinary_repetition_builds_tree_in_input_order() {
15256        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15257            let mut parser = mini_parser(repeated_x_tokens(3));
15258            let tree = parser
15259                .parse_atn_rule(&atn, 0)
15260                .expect("ordinary repetition should parse");
15261
15262            let root = parser
15263                .node(tree)
15264                .as_rule()
15265                .expect("entry result should be a rule");
15266            let body_rules = root.child_rules(1).collect::<Vec<_>>();
15267            assert_eq!(root.text(), "xxx<EOF>");
15268            assert_eq!(body_rules.len(), 3);
15269            assert_eq!(
15270                body_rules
15271                    .iter()
15272                    .map(|rule| rule.start_id().expect("body start").index())
15273                    .collect::<Vec<_>>(),
15274                [0, 1, 2]
15275            );
15276            assert_eq!(
15277                body_rules
15278                    .iter()
15279                    .map(|rule| rule.stop_id().expect("body stop").index())
15280                    .collect::<Vec<_>>(),
15281                [0, 1, 2]
15282            );
15283            assert_eq!(parser.number_of_syntax_errors(), 0);
15284        }
15285    }
15286
15287    #[test]
15288    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
15289        const DEPTH: usize = 20_000;
15290
15291        std::thread::Builder::new()
15292            .name("deferred-rule-materialization".to_owned())
15293            .stack_size(256 * 1024)
15294            .spawn(|| {
15295                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15296                let mut root = FastDeferredNodeId::EMPTY;
15297                for depth in 0..DEPTH {
15298                    root = parser
15299                        .recognition_arena
15300                        .deferred_rule_node(FastDeferredRule {
15301                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
15302                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
15303                            start_index: 0,
15304                            stop_index: None,
15305                            deferred_children: root,
15306                            children: NodeSeqId::EMPTY,
15307                        });
15308                }
15309
15310                let mut children = parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
15311                for expected_rule in (0..DEPTH).rev() {
15312                    let mut nodes = parser.recognition_arena.iter(children);
15313                    let node = nodes.next().expect("nested rule node");
15314                    assert!(nodes.next().is_none(), "each rule has one child");
15315                    let ArenaRecognizedNode::Rule {
15316                        rule_index,
15317                        children: nested,
15318                        ..
15319                    } = parser.recognition_arena.node(node)
15320                    else {
15321                        panic!("expected nested rule");
15322                    };
15323                    assert_eq!(rule_index as usize, expected_rule);
15324                    children = nested;
15325                }
15326                assert!(children.is_empty());
15327            })
15328            .expect("small-stack thread should start")
15329            .join()
15330            .expect("deferred rules should materialize without recursion");
15331    }
15332
15333    #[test]
15334    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
15335        const REPETITIONS: usize = 64;
15336
15337        let atn = ambiguous_ordinary_star_loop_atn();
15338        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15339        let tree = parser
15340            .parse_atn_rule(&atn, 0)
15341            .expect("ambiguous ordinary repetition should parse");
15342
15343        let root = parser
15344            .node(tree)
15345            .as_rule()
15346            .expect("entry result should be a rule");
15347        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
15348        assert_eq!(parser.input.index(), REPETITIONS);
15349        assert!(
15350            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
15351            "equivalent segmentations should keep deferred storage linear"
15352        );
15353        assert_eq!(parser.number_of_syntax_errors(), 0);
15354    }
15355
15356    #[test]
15357    fn long_ordinary_repetition_does_not_consume_native_stack() {
15358        const REPETITIONS: usize = 20_000;
15359
15360        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15361            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15362            parser.set_build_parse_trees(false);
15363            parser
15364                .parse_atn_rule(&atn, 0)
15365                .expect("long ordinary repetition should parse");
15366
15367            assert_eq!(parser.input.index(), REPETITIONS);
15368            assert_eq!(parser.number_of_syntax_errors(), 0);
15369        }
15370    }
15371
15372    #[test]
15373    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
15374        const REPETITIONS: usize = 2_000;
15375        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
15376
15377        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15378            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15379            let tree = parser
15380                .parse_atn_rule(&atn, 0)
15381                .expect("long rule repetition should parse");
15382
15383            let root = parser
15384                .node(tree)
15385                .as_rule()
15386                .expect("entry result should be a rule");
15387            assert_eq!(root.text(), expected_text);
15388            assert_eq!(root.child_rules(1).count(), REPETITIONS);
15389            let first_body = root.child_rules(1).next().expect("first body rule");
15390            let last_body = root.child_rules(1).next_back().expect("last body rule");
15391            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
15392            assert_eq!(
15393                last_body.stop_id().expect("last body stop").index(),
15394                REPETITIONS - 1
15395            );
15396
15397            let stats = parser.recognition_arena_stats();
15398            assert_eq!(
15399                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
15400                (REPETITIONS, REPETITIONS, 0)
15401            );
15402            assert_eq!(
15403                (stats.total_links, stats.live_links, stats.dead_links),
15404                (REPETITIONS, REPETITIONS, 0)
15405            );
15406            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
15407            assert_eq!(
15408                parser.recognition_arena.deferred_nodes.len(),
15409                REPETITIONS * 2 - 1
15410            );
15411            assert_eq!(parser.number_of_syntax_errors(), 0);
15412        }
15413    }
15414
15415    #[test]
15416    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
15417        let key = |state_number| FastRecognizeKey {
15418            state_number,
15419            stop_state: 10,
15420            index: state_number,
15421            rule_start_index: 0,
15422            decision_start_index: None,
15423            precedence: 0,
15424            recovery_symbols_id: 0,
15425            recovery_state: None,
15426        };
15427
15428        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15429        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
15430            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
15431        }
15432        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
15433        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
15434
15435        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15436        let repeated = key(1);
15437        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
15438            assert!(promote.clean_memo_enabled_for_key(&repeated));
15439        }
15440        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
15441
15442        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
15443            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
15444        }
15445        assert!(sparse.clean_memo_enabled_for_key(&repeated));
15446        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
15447        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
15448            assert!(sparse.clean_memo_enabled_for_key(&repeated));
15449        }
15450        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
15451    }
15452
15453    #[test]
15454    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
15455        assert_eq!(
15456            fast_recognize_memo_capacity(0),
15457            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15458        );
15459        assert_eq!(
15460            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
15461            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15462        );
15463        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
15464        assert_eq!(
15465            fast_recognize_memo_capacity(usize::MAX),
15466            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
15467        );
15468    }
15469
15470    #[test]
15471    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
15472        let mut scratch = FastRecognizeTopScratch::default();
15473        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15474        let retained_capacity = scratch.memo.capacity();
15475        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15476        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15477
15478        let larger_capacity = retained_capacity + 1;
15479        scratch.prepare(larger_capacity);
15480        let grown_capacity = scratch.memo.capacity();
15481        assert!(grown_capacity >= larger_capacity);
15482        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15483
15484        scratch.memo.insert(
15485            FastRecognizeKey {
15486                state_number: 0,
15487                stop_state: 0,
15488                index: 0,
15489                rule_start_index: 0,
15490                decision_start_index: None,
15491                precedence: 0,
15492                recovery_symbols_id: 0,
15493                recovery_state: None,
15494            },
15495            Rc::from([FastRecognizeOutcome {
15496                index: 0,
15497                consumed_eof: false,
15498                diagnostics: DiagnosticSeqId::EMPTY,
15499                deferred_nodes: FastDeferredNodeId::EMPTY,
15500                nodes: NodeSeqId::EMPTY,
15501            }]),
15502        );
15503        scratch.release_oversized_memo();
15504        assert!(scratch.memo.is_empty());
15505        assert_eq!(scratch.memo.capacity(), grown_capacity);
15506
15507        scratch
15508            .memo
15509            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
15510        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15511
15512        scratch.release_oversized_memo();
15513        assert!(scratch.memo.is_empty());
15514        assert_eq!(scratch.memo.capacity(), 0);
15515    }
15516
15517    #[test]
15518    fn clean_empty_multi_alt_outcomes_are_memoized() {
15519        let mut atn = ParserAtnBuilder::new(2);
15520        assert_eq!(
15521            atn.add_state(AtnStateKind::RuleStart, Some(0))
15522                .expect("state")
15523                .index(),
15524            0
15525        );
15526        assert_eq!(
15527            atn.add_state(AtnStateKind::BlockStart, Some(0))
15528                .expect("state")
15529                .index(),
15530            1
15531        );
15532        assert_eq!(
15533            atn.add_state(AtnStateKind::RuleStop, Some(0))
15534                .expect("state")
15535                .index(),
15536            2
15537        );
15538        atn.set_rule_to_start_state(vec![0])
15539            .expect("rule start states");
15540        atn.set_rule_to_stop_state(vec![2])
15541            .expect("rule stop states");
15542        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15543            .expect("transition");
15544        atn.add_transition(
15545            1,
15546            ParserTransitionSpec::Atom {
15547                target: 2,
15548                label: 1,
15549            },
15550        )
15551        .expect("transition");
15552        atn.add_transition(
15553            1,
15554            ParserTransitionSpec::Atom {
15555                target: 2,
15556                label: 2,
15557            },
15558        )
15559        .expect("transition");
15560        let atn = finish_atn(atn);
15561
15562        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15563        parser.fast_recovery_enabled = false;
15564        let mut visiting = FxHashSet::default();
15565        let mut memo = FxHashMap::default();
15566        let mut expected = ExpectedTokens::default();
15567        let outcomes = parser.recognize_state_fast(
15568            &atn,
15569            FastRecognizeRequest {
15570                state_number: 1,
15571                stop_state: 2,
15572                index: 0,
15573                rule_start_index: 0,
15574                decision_start_index: None,
15575                precedence: 0,
15576                depth: 0,
15577                recovery_symbols: parser.empty_recovery_symbols(),
15578                recovery_state: None,
15579            },
15580            FastRecognizeScratch {
15581                predicate_context: None,
15582                visiting: &mut visiting,
15583                memo: &mut memo,
15584                expected: &mut expected,
15585            },
15586        );
15587
15588        assert!(outcomes.is_empty());
15589        assert_eq!(memo.len(), 1);
15590        assert!(memo.values().next().expect("memo entry").is_empty());
15591
15592        parser.clean_memo_mode = CleanMemoMode::Sparse;
15593        visiting.clear();
15594        memo.clear();
15595        expected = ExpectedTokens::default();
15596        let sparse_outcomes = parser.recognize_state_fast(
15597            &atn,
15598            FastRecognizeRequest {
15599                state_number: 1,
15600                stop_state: 2,
15601                index: 0,
15602                rule_start_index: 0,
15603                decision_start_index: None,
15604                precedence: 0,
15605                depth: 0,
15606                recovery_symbols: parser.empty_recovery_symbols(),
15607                recovery_state: None,
15608            },
15609            FastRecognizeScratch {
15610                predicate_context: None,
15611                visiting: &mut visiting,
15612                memo: &mut memo,
15613                expected: &mut expected,
15614            },
15615        );
15616
15617        assert!(sparse_outcomes.is_empty());
15618        assert!(memo.is_empty());
15619    }
15620
15621    #[test]
15622    fn wildcard_matches_non_eof_only() {
15623        let mut parser = mini_parser(vec![
15624            TestToken::new(1).with_text("x"),
15625            TestToken::eof("parser-test", 1, 1, 1),
15626        ]);
15627        let matched = parser.match_wildcard().expect("wildcard");
15628        assert_eq!(parser.node(matched).text(), "x");
15629        assert!(parser.match_wildcard().is_err());
15630    }
15631
15632    #[test]
15633    fn add_parse_child_records_match_even_without_tree_building() {
15634        // `sync_decision`'s "is the current context empty" flag must reflect real
15635        // matches, not parse-tree children: when `build_parse_trees(false)`,
15636        // `children` stays empty but `has_matched_child` must still flip so nested
15637        // recovery does not wrongly suppress single-token deletion.
15638        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15639        let token = TestToken::new(1).with_text("x");
15640
15641        parser.set_build_parse_trees(false);
15642        let mut ctx = ParserRuleContext::new(0, 0);
15643        assert!(!ctx.has_matched_child());
15644        let child = parser.terminal_tree(token.id);
15645        parser.add_parse_child(&mut ctx, child);
15646        // Tree building is off, so no child is stored...
15647        assert_eq!(ctx.child_count(), 0);
15648        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15649        // ...but the match is recorded, so the context is no longer "empty".
15650        assert!(ctx.has_matched_child());
15651
15652        // With tree building on, the child is stored and the match is recorded.
15653        parser.set_build_parse_trees(true);
15654        let mut ctx = ParserRuleContext::new(0, 0);
15655        let child = parser.terminal_tree(token.id);
15656        parser.add_parse_child(&mut ctx, child);
15657        assert_eq!(ctx.child_count(), 1);
15658        assert!(ctx.has_matched_child());
15659    }
15660
15661    #[test]
15662    fn disabled_tree_building_does_not_grow_flat_storage() {
15663        let mut parser = mini_parser(vec![
15664            TestToken::new(1).with_text("x"),
15665            TestToken::new(1).with_text("y"),
15666            TestToken::eof("parser-test", 2, 1, 2),
15667        ]);
15668        parser.set_build_parse_trees(false);
15669        let mut context = ParserRuleContext::new(0, -1);
15670
15671        for _ in 0..2 {
15672            let child = parser.match_token(1).expect("token should match");
15673            parser.add_parse_child(&mut context, child);
15674        }
15675        let current = parser.input.lt_id(1).expect("EOF token");
15676        let error = parser.error_tree(current);
15677        parser.add_parse_child(&mut context, error);
15678        let root = parser.rule_node(context);
15679
15680        assert_eq!(
15681            parser.parse_tree_storage().stats(),
15682            ParseTreeStats::default()
15683        );
15684        assert!(
15685            parser
15686                .parse_tree_storage()
15687                .node(parser.token_store(), root)
15688                .is_none(),
15689            "the no-tree sentinel must not resolve to stored data"
15690        );
15691    }
15692
15693    #[test]
15694    fn disabled_tree_building_skips_recognition_rule_node_storage() {
15695        let atn = ordinary_star_loop_atn();
15696        let mut parser = mini_parser(repeated_x_tokens(3));
15697        parser.set_build_parse_trees(false);
15698
15699        parser
15700            .parse_atn_rule(&atn, 0)
15701            .expect("ordinary repetition should parse without a tree");
15702
15703        assert_eq!(parser.input.index(), 3);
15704        assert!(parser.recognition_arena.nodes.is_empty());
15705        assert!(parser.recognition_arena.seq_links.is_empty());
15706        assert!(parser.recognition_arena.deferred_nodes.is_empty());
15707        assert!(parser.recognition_arena.deferred_rules.is_empty());
15708        assert!(!parser.fast_token_nodes_enabled);
15709        assert!(parser.fast_recognize_scratch.memo.is_empty());
15710    }
15711
15712    #[test]
15713    fn parser_interprets_simple_atn_rule() {
15714        let atn = token_then_eof_atn();
15715        let mut parser = mini_parser(vec![
15716            TestToken::new(1).with_text("x"),
15717            TestToken::eof("parser-test", 1, 1, 1),
15718        ]);
15719
15720        let tree = parser
15721            .parse_atn_rule(&atn, 0)
15722            .expect("artificial parser rule should parse");
15723        assert_eq!(parser.node(tree).text(), "x<EOF>");
15724        assert_eq!(parser.number_of_syntax_errors(), 0);
15725        assert_eq!(
15726            parser
15727                .node(tree)
15728                .first_rule_stop(0)
15729                .expect("rule should stop at EOF")
15730                .token_type(),
15731            TOKEN_EOF
15732        );
15733
15734        let mut parser = mini_parser(vec![
15735            TestToken::new(1).with_text("x"),
15736            TestToken::eof("parser-test", 1, 1, 1),
15737        ]);
15738        let (tree, actions) = parser
15739            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15740            .expect("runtime-option parser rule should parse");
15741        assert!(actions.is_empty());
15742        assert_eq!(
15743            parser
15744                .node(tree)
15745                .first_rule_stop(0)
15746                .expect("rule should stop at EOF")
15747                .token_type(),
15748            TOKEN_EOF
15749        );
15750    }
15751
15752    #[test]
15753    fn runtime_options_default_ignores_noop_action_transitions() {
15754        let atn = noop_action_then_token_then_eof_atn();
15755        let mut parser = mini_parser(vec![
15756            TestToken::new(1).with_text("x"),
15757            TestToken::eof("parser-test", 1, 1, 1),
15758        ]);
15759
15760        let (tree, actions) = parser
15761            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15762            .expect("no-op parser action should not force action replay");
15763
15764        assert_eq!(parser.node(tree).text(), "x<EOF>");
15765        assert!(
15766            actions.is_empty(),
15767            "action_index=None transitions are ANTLR metadata, not replay actions"
15768        );
15769        assert_eq!(parser.number_of_syntax_errors(), 0);
15770    }
15771
15772    #[test]
15773    fn parser_exposes_buffered_token_stream_after_parse() {
15774        let atn = token_then_eof_atn();
15775        let mut parser = mini_parser(vec![
15776            TestToken::new(1).with_text("x"),
15777            TestToken::eof("parser-test", 1, 1, 1),
15778        ]);
15779
15780        let tree = parser
15781            .parse_atn_rule(&atn, 0)
15782            .expect("artificial parser rule should parse");
15783        assert_eq!(parser.node(tree).text(), "x<EOF>");
15784
15785        let stream = parser.token_stream();
15786        let source_index_after_parse = stream.token_source().index;
15787        let buffered = stream.tokens().collect::<Vec<_>>();
15788        assert_eq!(buffered.len(), 2);
15789        assert_eq!(buffered[0].text(), "x");
15790        assert_eq!(buffered[0].token_id().index(), 0);
15791        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
15792        assert_eq!(stream.token_source().index, source_index_after_parse);
15793        drop(buffered);
15794
15795        let stream = parser.into_token_stream();
15796        assert_eq!(stream.token_source().index, source_index_after_parse);
15797        assert_eq!(stream.tokens().next().expect("first token").text(), "x");
15798        assert_eq!(
15799            stream.tokens().nth(1).expect("EOF token").token_type(),
15800            TOKEN_EOF
15801        );
15802    }
15803
15804    #[test]
15805    fn parser_syntax_error_count_tracks_interpreted_recovery() {
15806        let atn = token_then_eof_atn();
15807        let mut parser = mini_parser(vec![
15808            TestToken::new(1).with_text("x"),
15809            TestToken::new(2).with_text("y"),
15810            TestToken::eof("parser-test", 2, 1, 2),
15811        ]);
15812
15813        let tree = parser
15814            .parse_atn_rule(&atn, 0)
15815            .expect("invalid token should recover into an error node");
15816
15817        assert_eq!(parser.number_of_syntax_errors(), 1);
15818        assert_eq!(
15819            parser
15820                .node(tree)
15821                .first_error_token()
15822                .expect("recovery should embed an error token")
15823                .text(),
15824            "y"
15825        );
15826    }
15827
15828    #[test]
15829    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
15830        let atn = token_then_eof_atn();
15831        let mut parser = mini_parser(vec![
15832            TestToken::new(2).with_text("y"),
15833            TestToken::eof("parser-test", 1, 1, 1),
15834        ]);
15835
15836        let error = parser
15837            .parse_atn_rule(&atn, 0)
15838            .expect_err("start-rule mismatch should remain a parser error");
15839
15840        assert_eq!(parser.number_of_syntax_errors(), 1);
15841        assert!(matches!(error, AntlrError::ParserError { .. }));
15842    }
15843
15844    #[test]
15845    fn adaptive_direct_rule_uses_simulator_decision() {
15846        let atn = two_alt_decision_atn();
15847        let mut simulator = ParserAtnSimulator::new(&atn);
15848        let mut parser = mini_parser(vec![
15849            TestToken::new(2).with_text("y"),
15850            TestToken::eof("parser-test", 1, 1, 1),
15851        ]);
15852
15853        let tree = parser
15854            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15855            .expect("direct adaptive rule should parse");
15856
15857        assert_eq!(parser.node(tree).text(), "y");
15858        assert_eq!(parser.input.index(), 1);
15859    }
15860
15861    #[test]
15862    fn adaptive_direct_rule_restores_input_on_fallback() {
15863        let atn = predicate_after_token_atn();
15864        let mut simulator = ParserAtnSimulator::new(&atn);
15865        let mut parser = mini_parser(vec![
15866            TestToken::new(1).with_text("x"),
15867            TestToken::new(2).with_text("y"),
15868            TestToken::eof("parser-test", 2, 1, 2),
15869        ]);
15870
15871        let tree = parser
15872            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15873            .expect("fallback recognizer should parse");
15874
15875        assert_eq!(parser.node(tree).text(), "xy");
15876        assert_eq!(parser.input.index(), 2);
15877        let stats = parser.parse_tree_storage().stats();
15878        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
15879        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
15880        assert_eq!(stats.scratch_links, 0);
15881    }
15882
15883    #[test]
15884    fn unknown_predicate_policy_defaults_to_assume_true() {
15885        let atn = predicate_after_token_atn();
15886        let mut parser = mini_parser(vec![
15887            TestToken::new(1).with_text("x"),
15888            TestToken::new(2).with_text("y"),
15889            TestToken::eof("parser-test", 2, 1, 2),
15890        ]);
15891
15892        let (tree, _) = parser
15893            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15894            .expect("unknown predicate should pass under the default policy");
15895
15896        assert_eq!(parser.node(tree).text(), "xy");
15897        assert_eq!(parser.number_of_syntax_errors(), 0);
15898    }
15899
15900    #[test]
15901    fn predicate_gated_same_lookahead_uses_viable_alternative() {
15902        let atn = predicate_gated_same_lookahead_atn([0, 1]);
15903        let mut parser = mini_parser(vec![
15904            TestToken::new(1).with_text("x"),
15905            TestToken::eof("parser-test", 1, 1, 1),
15906        ]);
15907
15908        let (tree, _) = parser
15909            .parse_atn_rule_with_runtime_options(
15910                &atn,
15911                0,
15912                ParserRuntimeOptions {
15913                    predicates: &[
15914                        (0, 0, ParserPredicate::False),
15915                        (0, 1, ParserPredicate::True),
15916                    ],
15917                    ..ParserRuntimeOptions::default()
15918                },
15919            )
15920            .expect("the second predicate-gated alternative should match");
15921
15922        assert_eq!(parser.node(tree).text(), "x<EOF>");
15923        assert_eq!(parser.number_of_syntax_errors(), 0);
15924        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
15925        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
15926    }
15927
15928    #[test]
15929    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
15930        // A generated parent may record an unknown-predicate coordinate, then
15931        // descend into an interpreted child. The child's interpreter entry must
15932        // not wipe the parent's recorded hit before the top-level surfaces it.
15933        let atn = token_then_eof_atn();
15934        let mut parser = mini_parser(vec![
15935            TestToken::new(1).with_text("x"),
15936            TestToken::eof("parser-test", 1, 1, 1),
15937        ]);
15938
15939        // Simulate the parent having recorded a fail-loud coordinate.
15940        parser.unknown_predicate_hits.push((7, 3));
15941
15942        // Run an interpreted child parse that records no coordinate of its own.
15943        parser
15944            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15945            .expect("child rule parses");
15946
15947        // The parent's coordinate must still be present for the top-level entry.
15948        let error = parser
15949            .take_unknown_semantic_error()
15950            .expect("parent's recorded coordinate must survive the nested interpreted parse");
15951        let AntlrError::Unsupported(message) = error else {
15952            panic!("expected AntlrError::Unsupported, got {error:?}");
15953        };
15954        assert!(message.contains("pred_index=3"), "message: {message}");
15955    }
15956
15957    #[test]
15958    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
15959        let atn = predicate_after_token_atn();
15960        let mut parser = mini_parser(vec![
15961            TestToken::new(1).with_text("x"),
15962            TestToken::new(2).with_text("y"),
15963            TestToken::eof("parser-test", 2, 1, 2),
15964        ]);
15965
15966        let result = parser.parse_atn_rule_with_runtime_options(
15967            &atn,
15968            0,
15969            ParserRuntimeOptions {
15970                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
15971                ..ParserRuntimeOptions::default()
15972            },
15973        );
15974
15975        assert!(
15976            result.is_err(),
15977            "the only path is predicate-guarded, so assume-false must fail the parse"
15978        );
15979    }
15980
15981    #[test]
15982    fn predicate_failure_message_keeps_semantic_recovery_path() {
15983        let atn = predicate_after_token_atn();
15984        let mut parser = mini_parser(vec![
15985            TestToken::new(1).with_text("x"),
15986            TestToken::new(2).with_text("y"),
15987            TestToken::eof("parser-test", 2, 1, 2),
15988        ]);
15989
15990        let (tree, _) = parser
15991            .parse_atn_rule_with_runtime_options(
15992                &atn,
15993                0,
15994                ParserRuntimeOptions {
15995                    predicates: &[(
15996                        0,
15997                        0,
15998                        ParserPredicate::FalseWithMessage {
15999                            message: "predicate rejected input",
16000                        },
16001                    )],
16002                    ..ParserRuntimeOptions::default()
16003                },
16004            )
16005            .expect("failure-message predicates recover through the semantic interpreter");
16006
16007        assert_eq!(parser.node(tree).text(), "xy");
16008        assert_eq!(parser.number_of_syntax_errors(), 1);
16009        assert!(
16010            parser.fast_predicate_cache.is_empty(),
16011            "failure-message predicates need the semantic interpreter's recovery outcome"
16012        );
16013    }
16014
16015    #[test]
16016    fn unknown_predicate_policy_error_names_the_coordinate() {
16017        let atn = predicate_after_token_atn();
16018        let mut parser = mini_parser(vec![
16019            TestToken::new(1).with_text("x"),
16020            TestToken::new(2).with_text("y"),
16021            TestToken::eof("parser-test", 2, 1, 2),
16022        ]);
16023
16024        let error = parser
16025            .parse_atn_rule_with_runtime_options(
16026                &atn,
16027                0,
16028                ParserRuntimeOptions {
16029                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16030                    ..ParserRuntimeOptions::default()
16031                },
16032            )
16033            .expect_err("evaluating an unknown predicate under Error policy must fail");
16034
16035        let AntlrError::Unsupported(message) = error else {
16036            panic!("expected AntlrError::Unsupported, got {error:?}");
16037        };
16038        assert!(
16039            message.contains("unsupported semantic predicate"),
16040            "message should name the failure class: {message}"
16041        );
16042        assert!(
16043            message.contains("pred_index=0"),
16044            "message should carry the coordinate: {message}"
16045        );
16046    }
16047
16048    #[test]
16049    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
16050        // A parser reused after a fail-loud parse must not carry the old
16051        // coordinates into a later parse. The fail-loud return keeps the hits
16052        // (so a generated parent can surface a recovered child's coordinate),
16053        // and the next parse's entry stashes/replaces them, so a subsequent
16054        // clean parse surfaces no stale error.
16055        let atn = predicate_after_token_atn();
16056        let mut parser = mini_parser(vec![
16057            TestToken::new(1).with_text("x"),
16058            TestToken::new(2).with_text("y"),
16059            TestToken::eof("parser-test", 2, 1, 2),
16060        ]);
16061
16062        parser
16063            .parse_atn_rule_with_runtime_options(
16064                &atn,
16065                0,
16066                ParserRuntimeOptions {
16067                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16068                    ..ParserRuntimeOptions::default()
16069                },
16070            )
16071            .expect_err("first parse fails loud under the Error policy");
16072
16073        // The failed parse kept its coordinate on the parser (so a generated
16074        // parent could surface a recovered child). A top-level reuse resets the
16075        // hits — generated parsers call `reset_unknown_semantic_hits` at their
16076        // public entry; direct interpreter-API callers do the same.
16077        parser.reset_unknown_semantic_hits();
16078        assert!(
16079            parser.take_unknown_semantic_error().is_none(),
16080            "reset must drop stale unknown-predicate coordinates before a reused parse"
16081        );
16082    }
16083
16084    #[derive(Debug, Default)]
16085    struct RecordingHooks {
16086        predicates: Vec<(usize, usize, usize, Option<String>)>,
16087        actions: Vec<(usize, String, Option<String>)>,
16088        action_trees: Vec<Option<String>>,
16089    }
16090
16091    impl SemanticHooks for RecordingHooks {
16092        fn sempred<S>(
16093            &mut self,
16094            ctx: &mut ParserSemCtx<'_, S>,
16095            rule_index: usize,
16096            pred_index: usize,
16097        ) -> Option<bool>
16098        where
16099            S: TokenSource,
16100        {
16101            self.predicates.push((
16102                ctx.input_index(),
16103                rule_index,
16104                pred_index,
16105                ctx.token_text(1).map(|token| token.text().to_owned()),
16106            ));
16107            Some(true)
16108        }
16109
16110        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
16111        where
16112            S: TokenSource,
16113        {
16114            self.actions.push((
16115                action.source_state(),
16116                ctx.action_text(),
16117                ctx.rule_name().map(str::to_owned),
16118            ));
16119            self.action_trees.push(ctx.tree().map(Node::text));
16120            true
16121        }
16122    }
16123
16124    #[derive(Debug, Default)]
16125    struct RejectingPredicateHooks {
16126        predicates: Vec<(usize, usize, usize, Option<String>)>,
16127    }
16128
16129    impl SemanticHooks for RejectingPredicateHooks {
16130        fn sempred<S>(
16131            &mut self,
16132            ctx: &mut ParserSemCtx<'_, S>,
16133            rule_index: usize,
16134            pred_index: usize,
16135        ) -> Option<bool>
16136        where
16137            S: TokenSource,
16138        {
16139            self.predicates.push((
16140                ctx.input_index(),
16141                rule_index,
16142                pred_index,
16143                ctx.token_text(1).map(|token| token.text().to_owned()),
16144            ));
16145            Some(false)
16146        }
16147    }
16148
16149    #[test]
16150    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
16151        let atn = predicate_gated_same_lookahead_atn([0, 0]);
16152        let mut parser = mini_parser_with_hooks(
16153            vec![
16154                TestToken::new(1).with_text("x"),
16155                TestToken::eof("parser-test", 1, 1, 1),
16156            ],
16157            RecordingHooks::default(),
16158        );
16159
16160        let (tree, _) = parser
16161            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16162            .expect("both alternatives share one replay-safe predicate result");
16163
16164        assert_eq!(parser.node(tree).text(), "x<EOF>");
16165        assert_eq!(
16166            parser.semantic_hooks.predicates,
16167            vec![(0, 0, 0, Some("x".to_owned()))]
16168        );
16169        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
16170    }
16171
16172    #[test]
16173    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
16174        let atn = predicate_after_token_atn();
16175        let mut parser = mini_parser_with_hooks(
16176            vec![
16177                TestToken::new(1).with_text("x"),
16178                TestToken::new(2).with_text("y"),
16179                TestToken::eof("parser-test", 2, 1, 2),
16180            ],
16181            RecordingHooks::default(),
16182        );
16183
16184        let (tree, _) = parser
16185            .parse_atn_rule_with_runtime_options(
16186                &atn,
16187                0,
16188                ParserRuntimeOptions {
16189                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16190                    ..ParserRuntimeOptions::default()
16191                },
16192            )
16193            .expect("hook supplies the missing predicate result");
16194
16195        assert_eq!(parser.node(tree).text(), "xy");
16196        assert_eq!(
16197            parser.semantic_hooks.predicates,
16198            vec![(1, 0, 0, Some("y".to_owned()))]
16199        );
16200        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
16201    }
16202
16203    #[test]
16204    fn runtime_options_default_preserves_semantic_hook_predicates() {
16205        let atn = predicate_after_token_atn();
16206        let mut parser = mini_parser_with_hooks(
16207            vec![
16208                TestToken::new(1).with_text("x"),
16209                TestToken::new(2).with_text("y"),
16210                TestToken::eof("parser-test", 2, 1, 2),
16211            ],
16212            RejectingPredicateHooks::default(),
16213        );
16214
16215        let result =
16216            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
16217
16218        assert!(
16219            result.is_err(),
16220            "default runtime options must not bypass semantic hooks for predicate ATNs"
16221        );
16222        assert_eq!(
16223            parser.semantic_hooks.predicates,
16224            vec![(1, 0, 0, Some("y".to_owned()))]
16225        );
16226        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
16227    }
16228
16229    #[test]
16230    fn semantic_hook_handles_committed_parser_action() {
16231        let atn = token_then_eof_atn();
16232        let mut parser = mini_parser_with_hooks(
16233            vec![
16234                TestToken::new(1).with_text("x"),
16235                TestToken::eof("parser-test", 1, 1, 1),
16236            ],
16237            RecordingHooks::default(),
16238        );
16239        let (tree, _) = parser
16240            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16241            .expect("rule parses before action hook is tested");
16242
16243        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16244        assert_eq!(
16245            parser.semantic_hooks.actions,
16246            vec![(42, "x".to_owned(), Some("s".to_owned()))]
16247        );
16248        assert_eq!(
16249            parser.semantic_hooks.action_trees,
16250            [Some("x<EOF>".to_owned())]
16251        );
16252    }
16253
16254    #[test]
16255    fn unhandled_committed_action_fails_loud_under_error_policy() {
16256        // An action offered to the hook that no hook handles (returns false)
16257        // must be recorded and surfaced as `AntlrError::Unsupported` under the
16258        // Error policy, so a `hook`-disposed action is not silently dropped.
16259        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16260        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16261        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
16262
16263        // DecliningHooks::action returns false (unhandled).
16264        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16265
16266        let error = parser
16267            .take_unknown_semantic_error()
16268            .expect("an unhandled committed action under Error policy must fail loud");
16269        let AntlrError::Unsupported(message) = error else {
16270            panic!("expected AntlrError::Unsupported, got {error:?}");
16271        };
16272        assert!(
16273            message.contains("unhandled semantic action") && message.contains("state=42"),
16274            "message should name the dropped action coordinate: {message}"
16275        );
16276
16277        // Under the default (assume-true) policy the same miss is not recorded.
16278        let mut lenient =
16279            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16280        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
16281        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16282        assert!(lenient.take_unknown_semantic_error().is_none());
16283    }
16284
16285    #[test]
16286    fn translated_predicate_is_unaffected_by_error_policy() {
16287        let atn = predicate_after_token_atn();
16288        let mut parser = mini_parser(vec![
16289            TestToken::new(1).with_text("x"),
16290            TestToken::new(2).with_text("y"),
16291            TestToken::eof("parser-test", 2, 1, 2),
16292        ]);
16293
16294        let (tree, _) = parser
16295            .parse_atn_rule_with_runtime_options(
16296                &atn,
16297                0,
16298                ParserRuntimeOptions {
16299                    predicates: &[(0, 0, ParserPredicate::True)],
16300                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16301                    ..ParserRuntimeOptions::default()
16302                },
16303            )
16304            .expect("a predicate covered by the table is not an unknown coordinate");
16305
16306        assert_eq!(parser.node(tree).text(), "xy");
16307    }
16308
16309    /// Hooks that decline (`None`) must fall through to the configured policy
16310    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
16311    /// legacy table path. Regression for the `unwrap_or(false)` that silently
16312    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
16313    fn hook_predicate_semantics() -> ParserSemantics {
16314        let mut ir = SemIr::new();
16315        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
16316        ParserSemantics {
16317            ir,
16318            predicates: vec![ParserSemanticPredicate {
16319                rule_index: 0,
16320                pred_index: 0,
16321                expr,
16322                failure_message: None,
16323            }],
16324            actions: Vec::new(),
16325        }
16326    }
16327
16328    #[derive(Debug, Default)]
16329    struct DecliningHooks;
16330
16331    impl SemanticHooks for DecliningHooks {}
16332
16333    #[test]
16334    fn semir_hook_none_falls_through_to_assume_true() {
16335        let atn = predicate_after_token_atn();
16336        let semantics = hook_predicate_semantics();
16337        let mut parser = mini_parser_with_hooks(
16338            vec![
16339                TestToken::new(1).with_text("x"),
16340                TestToken::new(2).with_text("y"),
16341                TestToken::eof("parser-test", 2, 1, 2),
16342            ],
16343            DecliningHooks,
16344        );
16345
16346        let (tree, _) = parser
16347            .parse_atn_rule_with_runtime_options(
16348                &atn,
16349                0,
16350                ParserRuntimeOptions {
16351                    semantics: Some(&semantics),
16352                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
16353                    ..ParserRuntimeOptions::default()
16354                },
16355            )
16356            .expect("a declined SemIR hook must pass under assume-true");
16357
16358        assert_eq!(parser.node(tree).text(), "xy");
16359    }
16360
16361    #[test]
16362    fn semir_hook_none_falls_through_to_assume_false() {
16363        let atn = predicate_after_token_atn();
16364        let semantics = hook_predicate_semantics();
16365        let mut parser = mini_parser_with_hooks(
16366            vec![
16367                TestToken::new(1).with_text("x"),
16368                TestToken::new(2).with_text("y"),
16369                TestToken::eof("parser-test", 2, 1, 2),
16370            ],
16371            DecliningHooks,
16372        );
16373
16374        let result = parser.parse_atn_rule_with_runtime_options(
16375            &atn,
16376            0,
16377            ParserRuntimeOptions {
16378                semantics: Some(&semantics),
16379                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16380                ..ParserRuntimeOptions::default()
16381            },
16382        );
16383
16384        assert!(
16385            result.is_err(),
16386            "a declined SemIR hook must fail the only guarded path under assume-false"
16387        );
16388    }
16389
16390    #[test]
16391    fn semir_hook_none_records_coordinate_under_error_policy() {
16392        let atn = predicate_after_token_atn();
16393        let semantics = hook_predicate_semantics();
16394        let mut parser = mini_parser_with_hooks(
16395            vec![
16396                TestToken::new(1).with_text("x"),
16397                TestToken::new(2).with_text("y"),
16398                TestToken::eof("parser-test", 2, 1, 2),
16399            ],
16400            DecliningHooks,
16401        );
16402
16403        let error = parser
16404            .parse_atn_rule_with_runtime_options(
16405                &atn,
16406                0,
16407                ParserRuntimeOptions {
16408                    semantics: Some(&semantics),
16409                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16410                    ..ParserRuntimeOptions::default()
16411                },
16412            )
16413            .expect_err("a declined SemIR hook under Error policy must fail the parse");
16414
16415        let AntlrError::Unsupported(message) = error else {
16416            panic!("expected AntlrError::Unsupported, got {error:?}");
16417        };
16418        assert!(
16419            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
16420            "message should name the unresolved coordinate: {message}"
16421        );
16422    }
16423
16424    #[test]
16425    fn generated_direct_predicate_honors_installed_policy() {
16426        // The generated recursive-descent path calls
16427        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
16428        // going through `ParserRuntimeOptions`, so the policy must be installed
16429        // via `set_unknown_predicate_policy` (as the generated constructor now
16430        // does). A declining hook must then honor it rather than the default.
16431        let semantics = hook_predicate_semantics();
16432        let context = ParserRuleContext::new(0, -1);
16433
16434        let mut assume_true =
16435            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16436        assert!(
16437            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
16438                &semantics, 0, 0, &context, 0
16439            ),
16440            "default AssumeTrue accepts a declined hook"
16441        );
16442        assert!(assume_true.take_unknown_semantic_error().is_none());
16443
16444        let mut error_policy =
16445            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16446        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16447        assert!(
16448            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
16449                &semantics, 0, 0, &context, 0
16450            ),
16451            "Error policy rejects a declined hook on the generated-direct path"
16452        );
16453        let error = error_policy
16454            .take_unknown_semantic_error()
16455            .expect("Error policy records the unresolved coordinate for the generated path");
16456        let AntlrError::Unsupported(message) = error else {
16457            panic!("expected AntlrError::Unsupported, got {error:?}");
16458        };
16459        assert!(message.contains("pred_index=0"), "message: {message}");
16460    }
16461
16462    #[test]
16463    fn parser_rule_start_skips_leading_hidden_tokens() {
16464        let atn = token_then_eof_atn();
16465        let mut parser = mini_parser(vec![
16466            TestToken::new(99)
16467                .with_text(" ")
16468                .with_channel(HIDDEN_CHANNEL),
16469            TestToken::new(1).with_text("x"),
16470            TestToken::eof("parser-test", 2, 1, 2),
16471        ]);
16472
16473        let tree = parser
16474            .parse_atn_rule(&atn, 0)
16475            .expect("artificial parser rule should parse");
16476        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
16477            panic!("rule node should be present");
16478        };
16479        assert_eq!(
16480            rule.start()
16481                .expect("rule should have a start token")
16482                .token_type(),
16483            1
16484        );
16485    }
16486
16487    #[test]
16488    fn parser_action_after_eof_stops_at_eof_token() {
16489        let atn = eof_then_action_atn();
16490        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16491
16492        let (_, actions) = parser
16493            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16494            .expect("EOF action rule should parse");
16495
16496        assert_eq!(actions.len(), 1);
16497        assert_eq!(actions[0].stop_index(), Some(0));
16498        assert_eq!(
16499            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
16500            ""
16501        );
16502    }
16503
16504    #[test]
16505    fn after_action_stop_uses_rule_context_stop_not_cursor() {
16506        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
16507        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
16508        // but the rule did not consume it. The @after stop must follow the rule
16509        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
16510        let mut id = TestToken::new(1).with_text("x");
16511        id.set_token_index(0);
16512        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
16513        eof.set_token_index(1);
16514        let mut parser = mini_parser(vec![id.clone(), eof]);
16515        // Advance the cursor onto EOF, as it would be after `a` matched ID.
16516        parser.consume();
16517        assert_eq!(parser.la(1), TOKEN_EOF);
16518
16519        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
16520        // exactly what finish_rule(consumed_eof = false) records.
16521        let mut ctx = ParserRuleContext::new(0, 0);
16522        parser.set_context_stop(
16523            &mut ctx,
16524            parser.token_id_at(0).expect("ID token should be buffered"),
16525        );
16526        let tree = parser.rule_node(ctx);
16527
16528        let current_index = parser.input.index();
16529        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
16530        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
16531        // ...but the tree-aware helper follows the rule context stop (ID).
16532        assert_eq!(
16533            parser.after_action_stop_index_for_tree(tree, current_index),
16534            Some(0)
16535        );
16536    }
16537
16538    #[test]
16539    fn after_action_start_uses_rule_context_start_not_cursor() {
16540        // A rule that begins after leading hidden-channel tokens: the rule context
16541        // start (set by `enter_rule`) is the first visible token, not the raw cursor
16542        // that may still point at the hidden prefix. The @after start must follow
16543        // the context start so `$start`/`$text` excludes the hidden prefix.
16544        let mut parser = mini_parser(vec![
16545            TestToken::new(9)
16546                .with_text(" ")
16547                .with_channel(HIDDEN_CHANNEL),
16548            TestToken::new(9)
16549                .with_text(" ")
16550                .with_channel(HIDDEN_CHANNEL),
16551            TestToken::new(1).with_text("x"),
16552            TestToken::eof("parser-test", 3, 1, 3),
16553        ]);
16554
16555        let mut ctx = ParserRuleContext::new(0, 0);
16556        parser.set_context_start(
16557            &mut ctx,
16558            parser.token_id_at(2).expect("ID token should be buffered"),
16559        );
16560        let tree = parser.rule_node(ctx);
16561
16562        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
16563        // ...but the tree-aware helper follows the rule context start (index 2).
16564        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
16565
16566        // With no rule start recorded, it falls back to the provided index.
16567        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
16568        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
16569    }
16570
16571    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
16572        FastRecognizeOutcome {
16573            index,
16574            consumed_eof,
16575            diagnostics: DiagnosticSeqId::EMPTY,
16576            deferred_nodes: FastDeferredNodeId::EMPTY,
16577            nodes: NodeSeqId(marker),
16578        }
16579    }
16580
16581    #[test]
16582    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
16583        let mut outcomes = vec![
16584            clean_fast_outcome(4, false, 0),
16585            clean_fast_outcome(2, false, 1),
16586            clean_fast_outcome(4, false, 2),
16587            clean_fast_outcome(4, true, 3),
16588            clean_fast_outcome(2, false, 4),
16589        ];
16590        let mut scratch = FastOutcomeDedupScratch::default();
16591
16592        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16593
16594        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
16595        assert_eq!(
16596            outcomes
16597                .iter()
16598                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
16599                .collect::<Vec<_>>(),
16600            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
16601        );
16602        assert!(scratch.dense_words.is_empty());
16603        assert!(scratch.sparse_keys.is_empty());
16604    }
16605
16606    #[test]
16607    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
16608        let mut scratch = FastOutcomeDedupScratch::default();
16609        let mut outcomes = (100..109)
16610            .flat_map(|index| {
16611                [
16612                    clean_fast_outcome(
16613                        index,
16614                        false,
16615                        u32::try_from(index).expect("test index fits in u32"),
16616                    ),
16617                    clean_fast_outcome(index, false, u32::MAX),
16618                ]
16619            })
16620            .collect();
16621
16622        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16623
16624        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16625        assert_eq!(outcomes.len(), 9);
16626        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
16627        let dense_capacity = scratch.dense_words.capacity();
16628
16629        let mut reused = (1_000..1_009)
16630            .map(|index| {
16631                clean_fast_outcome(
16632                    index,
16633                    false,
16634                    u32::try_from(index).expect("test index fits in u32"),
16635                )
16636            })
16637            .collect();
16638        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16639
16640        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16641        assert_eq!(reused.len(), 9);
16642        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
16643    }
16644
16645    #[test]
16646    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
16647        let mut scratch = FastOutcomeDedupScratch::default();
16648        let sparse_indexes = [
16649            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
16650        ];
16651        let mut outcomes = sparse_indexes
16652            .into_iter()
16653            .chain([400_000])
16654            .enumerate()
16655            .map(|(marker, index)| {
16656                clean_fast_outcome(
16657                    index,
16658                    false,
16659                    u32::try_from(marker).expect("test marker fits in u32"),
16660                )
16661            })
16662            .collect();
16663
16664        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16665
16666        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16667        assert_eq!(outcomes.len(), sparse_indexes.len());
16668        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
16669        let sparse_capacity = scratch.sparse_keys.capacity();
16670
16671        let mut reused = sparse_indexes
16672            .into_iter()
16673            .map(|index| {
16674                clean_fast_outcome(
16675                    index,
16676                    false,
16677                    u32::try_from(index).expect("test index fits in u32"),
16678                )
16679            })
16680            .collect();
16681        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16682
16683        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16684        assert_eq!(reused.len(), sparse_indexes.len());
16685        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
16686    }
16687
16688    #[test]
16689    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
16690        let mut scratch = FastOutcomeDedupScratch::default();
16691        scratch
16692            .sparse_keys
16693            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
16694        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16695        let mut outcomes = (0..9)
16696            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
16697            .collect();
16698
16699        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16700
16701        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16702        assert!(scratch.sparse_keys.is_empty());
16703        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16704    }
16705
16706    #[test]
16707    fn fast_outcome_selection_respects_sll_tie_order() {
16708        let mut arena = RecognitionArena::default();
16709        let first = FastRecognizeOutcome {
16710            index: 1,
16711            consumed_eof: false,
16712            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16713                line: 1,
16714                column: 0,
16715                message: "mismatched input 'x'".to_owned(),
16716            }]),
16717            deferred_nodes: FastDeferredNodeId::EMPTY,
16718            nodes: NodeSeqId::EMPTY,
16719        };
16720        let second = FastRecognizeOutcome {
16721            index: first.index,
16722            consumed_eof: first.consumed_eof,
16723            diagnostics: DiagnosticSeqId::EMPTY,
16724            deferred_nodes: FastDeferredNodeId::EMPTY,
16725            nodes: NodeSeqId::EMPTY,
16726        };
16727
16728        let selected = select_best_fast_outcome(
16729            [first, second].into_iter(),
16730            PredictionMode::Sll,
16731            None,
16732            |_| panic!("caller-follow token probe should not run"),
16733            &arena,
16734        )
16735        .expect("one outcome should be selected");
16736        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
16737        let eof_second = FastRecognizeOutcome {
16738            index: second.index,
16739            consumed_eof: true,
16740            diagnostics: DiagnosticSeqId::EMPTY,
16741            deferred_nodes: FastDeferredNodeId::EMPTY,
16742            nodes: NodeSeqId::EMPTY,
16743        };
16744        let selected = select_best_fast_outcome(
16745            [first, eof_second].into_iter(),
16746            PredictionMode::Sll,
16747            None,
16748            |_| panic!("caller-follow token probe should not run"),
16749            &arena,
16750        )
16751        .expect("one outcome should be selected");
16752        assert!(!selected.consumed_eof);
16753        let selected = select_best_fast_outcome(
16754            [first, second].into_iter(),
16755            PredictionMode::Ll,
16756            None,
16757            |_| panic!("caller-follow token probe should not run"),
16758            &arena,
16759        )
16760        .expect("one outcome should be selected");
16761        assert!(selected.diagnostics.is_empty());
16762    }
16763
16764    #[test]
16765    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
16766        let mut arena = RecognitionArena::default();
16767        let first = FastRecognizeOutcome {
16768            index: 3,
16769            consumed_eof: false,
16770            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16771                line: 1,
16772                column: 0,
16773                message: "mismatched input 'x' expecting 'a'".to_owned(),
16774            }]),
16775            deferred_nodes: FastDeferredNodeId::EMPTY,
16776            nodes: NodeSeqId::EMPTY,
16777        };
16778        let same_rank = FastRecognizeOutcome {
16779            index: first.index,
16780            consumed_eof: first.consumed_eof,
16781            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16782                line: 1,
16783                column: 0,
16784                message: "mismatched input 'x' expecting 'b'".to_owned(),
16785            }]),
16786            deferred_nodes: FastDeferredNodeId::EMPTY,
16787            nodes: NodeSeqId::EMPTY,
16788        };
16789        let better_rank = FastRecognizeOutcome {
16790            index: first.index,
16791            consumed_eof: first.consumed_eof,
16792            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16793                line: 1,
16794                column: 0,
16795                message: "missing 'a' at 'x'".to_owned(),
16796            }]),
16797            deferred_nodes: FastDeferredNodeId::EMPTY,
16798            nodes: NodeSeqId::EMPTY,
16799        };
16800        let mut outcomes = vec![first, same_rank, better_rank];
16801
16802        dedupe_fast_outcomes(&mut outcomes, &arena);
16803
16804        assert_eq!(outcomes.len(), 2);
16805        assert_eq!(
16806            arena
16807                .diagnostics(outcomes[0].diagnostics)
16808                .next()
16809                .expect("first diagnostic")
16810                .message,
16811            "mismatched input 'x' expecting 'a'"
16812        );
16813        assert_eq!(
16814            arena
16815                .diagnostics(outcomes[1].diagnostics)
16816                .next()
16817                .expect("second diagnostic")
16818                .message,
16819            "missing 'a' at 'x'"
16820        );
16821    }
16822
16823    #[test]
16824    fn fast_outcome_selection_prefers_generated_caller_follow() {
16825        let arena = RecognitionArena::default();
16826        let earlier = FastRecognizeOutcome {
16827            index: 7,
16828            consumed_eof: false,
16829            diagnostics: DiagnosticSeqId::EMPTY,
16830            deferred_nodes: FastDeferredNodeId::EMPTY,
16831            nodes: NodeSeqId::EMPTY,
16832        };
16833        let later = FastRecognizeOutcome {
16834            index: 8,
16835            consumed_eof: false,
16836            diagnostics: DiagnosticSeqId::EMPTY,
16837            deferred_nodes: FastDeferredNodeId::EMPTY,
16838            nodes: NodeSeqId::EMPTY,
16839        };
16840        let mut follow = TokenBitSet::default();
16841        follow.insert(5);
16842
16843        let selected = select_best_fast_outcome(
16844            [later, earlier].into_iter(),
16845            PredictionMode::Ll,
16846            Some(&follow),
16847            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
16848            &arena,
16849        )
16850        .expect("one outcome should be selected");
16851        assert_eq!(selected.index, 7);
16852
16853        let selected = select_best_fast_outcome(
16854            [later, earlier].into_iter(),
16855            PredictionMode::Ll,
16856            Some(&follow),
16857            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
16858            &arena,
16859        )
16860        .expect("one outcome should be selected");
16861        assert_eq!(selected.index, 8);
16862
16863        let indented_next_statement = FastRecognizeOutcome {
16864            index: 9,
16865            consumed_eof: false,
16866            diagnostics: DiagnosticSeqId::EMPTY,
16867            deferred_nodes: FastDeferredNodeId::EMPTY,
16868            nodes: NodeSeqId::EMPTY,
16869        };
16870        let selected = select_best_fast_outcome(
16871            [indented_next_statement, earlier].into_iter(),
16872            PredictionMode::Ll,
16873            Some(&follow),
16874            |index| {
16875                let is_boundary = index == 7;
16876                let is_boundary_gap = matches!(index, 7 | 8);
16877                (
16878                    if index == 7 { 5 } else { TOKEN_EOF },
16879                    is_boundary,
16880                    is_boundary_gap,
16881                )
16882            },
16883            &arena,
16884        )
16885        .expect("one outcome should be selected");
16886        assert_eq!(selected.index, 7);
16887
16888        let continuation = FastRecognizeOutcome {
16889            index: 10,
16890            consumed_eof: false,
16891            diagnostics: DiagnosticSeqId::EMPTY,
16892            deferred_nodes: FastDeferredNodeId::EMPTY,
16893            nodes: NodeSeqId::EMPTY,
16894        };
16895        let selected = select_best_fast_outcome(
16896            [continuation, earlier].into_iter(),
16897            PredictionMode::Ll,
16898            Some(&follow),
16899            |index| {
16900                let is_boundary = matches!(index, 7 | 9);
16901                (
16902                    if index == 7 { 5 } else { TOKEN_EOF },
16903                    is_boundary,
16904                    is_boundary,
16905                )
16906            },
16907            &arena,
16908        )
16909        .expect("one outcome should be selected");
16910        assert_eq!(selected.index, 10);
16911
16912        let selected = select_best_fast_outcome(
16913            [earlier, later].into_iter(),
16914            PredictionMode::Sll,
16915            Some(&follow),
16916            |_| panic!("caller-follow token probe should not run in SLL mode"),
16917            &arena,
16918        )
16919        .expect("one outcome should be selected");
16920        assert_eq!(selected.index, 8);
16921    }
16922
16923    #[test]
16924    fn caller_follow_boundary_text_requires_separator_shape() {
16925        assert!(is_caller_follow_boundary_text(";"));
16926        assert!(is_caller_follow_boundary_text("\n"));
16927        assert!(is_caller_follow_boundary_text("\r\n  "));
16928        assert!(is_caller_follow_boundary_text(";\n"));
16929        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
16930        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
16931        assert!(!is_caller_follow_boundary_text("identifier"));
16932        assert!(is_caller_follow_boundary_gap_text(" \t "));
16933        assert!(is_caller_follow_boundary_gap_text("\n  "));
16934        assert!(is_caller_follow_boundary_gap_text(";\t"));
16935        assert!(!is_caller_follow_boundary_gap_text(
16936            "\"\"\"line1\nline2\"\"\""
16937        ));
16938        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
16939    }
16940
16941    #[test]
16942    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
16943        let mut parser = mini_parser(vec![
16944            TestToken::new(5).with_text("\n"),
16945            TestToken::new(6)
16946                .with_text("// comment\n")
16947                .with_channel(HIDDEN_CHANNEL),
16948            TestToken::new(1).with_text("x"),
16949            TestToken::eof("parser-test", 1, 2, 0),
16950        ]);
16951
16952        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
16953        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
16954        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
16955    }
16956
16957    #[test]
16958    fn caller_follow_token_info_uses_stream_visible_channel() {
16959        let source = Source {
16960            tokens: vec![
16961                TestToken::new(5).with_text("\n").with_channel(2),
16962                TestToken::new(1).with_text("x").with_channel(2),
16963                TestToken::new(6)
16964                    .with_text("// comment\n")
16965                    .with_channel(HIDDEN_CHANNEL),
16966                TestToken::eof("parser-test", 1, 2, 0),
16967            ],
16968            index: 0,
16969        };
16970        let data = RecognizerData::new(
16971            "Mini.g4",
16972            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16973        );
16974        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
16975
16976        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
16977        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
16978        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
16979    }
16980
16981    #[test]
16982    fn reset_per_parse_caches_clears_state_expected_token_cache() {
16983        let atn = token_then_eof_atn();
16984        let mut parser = mini_parser(Vec::new());
16985
16986        let _ = parser.cached_state_expected_token_set(&atn, 0);
16987        assert!(!parser.state_expected_token_cache.is_empty());
16988
16989        parser.reset_per_parse_caches();
16990        assert!(parser.state_expected_token_cache.is_empty());
16991    }
16992
16993    #[test]
16994    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
16995        let cyclic = epsilon_cycle_atn();
16996        let acyclic = token_then_eof_atn();
16997        let mut parser = mini_parser(Vec::new());
16998
16999        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17000        assert_eq!(
17001            parser.empty_cycle_cache_atn,
17002            Some(SharedAtnCacheKey::for_atn(&cyclic))
17003        );
17004        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17005
17006        parser.reset_per_parse_caches();
17007        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17008        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17009
17010        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
17011        assert_eq!(
17012            parser.empty_cycle_cache_atn,
17013            Some(SharedAtnCacheKey::for_atn(&acyclic))
17014        );
17015        assert_eq!(parser.empty_cycle_cache[1], Some(false));
17016    }
17017
17018    #[test]
17019    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
17020        let source = Source {
17021            tokens: vec![
17022                TestToken::new(1).with_text("x"),
17023                TestToken::eof("parser-test", 1, 1, 1),
17024            ],
17025            index: 0,
17026        };
17027        let data = RecognizerData::new(
17028            "Mini.g4",
17029            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17030        );
17031        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17032        let expected = ExpectedTokens {
17033            index: Some(0),
17034            symbols: BTreeSet::new(),
17035            no_viable: None,
17036        };
17037
17038        let (_, message) = parser.expected_error_message(0, 0, &expected);
17039
17040        assert_eq!(message, "mismatched input 'x'");
17041    }
17042
17043    #[test]
17044    fn eof_rule_stop_index_points_at_eof_token() {
17045        let source = Source {
17046            tokens: vec![
17047                TestToken::new(1).with_text("x"),
17048                TestToken::eof("parser-test", 1, 1, 1),
17049            ],
17050            index: 0,
17051        };
17052        let data = RecognizerData::new(
17053            "Mini.g4",
17054            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17055        );
17056        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17057
17058        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
17059        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
17060    }
17061
17062    #[test]
17063    fn generated_parser_action_uses_current_rule_stop_boundary() {
17064        let mut parser = mini_parser(vec![
17065            TestToken::new(1).with_text("x"),
17066            TestToken::eof("parser-test", 1, 1, 1),
17067        ]);
17068
17069        parser.match_token(1).expect("token should match");
17070        let action = parser.parser_action_at_current(7, 0, 0, false);
17071        assert_eq!(action.source_state(), 7);
17072        assert_eq!(action.rule_index(), 0);
17073        assert_eq!(action.start_index(), 0);
17074        assert_eq!(action.stop_index(), Some(0));
17075
17076        parser.match_eof().expect("EOF should match");
17077        let action = parser.parser_action_at_current(8, 0, 0, true);
17078        assert_eq!(action.stop_index(), Some(1));
17079    }
17080
17081    #[test]
17082    fn folds_left_recursive_boundary_into_rule_node() {
17083        let mut arena = RecognitionArena::default();
17084        let first = arena.push_node(ArenaRecognizedNode::Token {
17085            token: TokenId::try_from(0).expect("test token ID"),
17086        });
17087        let boundary =
17088            arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: 1 });
17089        let second = arena.push_node(ArenaRecognizedNode::Token {
17090            token: TokenId::try_from(1).expect("test token ID"),
17091        });
17092        let mut nodes = NodeSeqId::EMPTY;
17093        for node in [first, boundary, second].into_iter().rev() {
17094            nodes = arena.prepend(nodes, node);
17095        }
17096
17097        let folded = arena.fold_left_recursive_boundaries(nodes);
17098        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
17099
17100        assert_eq!(folded_nodes.len(), 2);
17101        let ArenaRecognizedNode::Rule {
17102            rule_index,
17103            invoking_state,
17104            start_index,
17105            stop_index,
17106            children,
17107            ..
17108        } = arena.node(folded_nodes[0])
17109        else {
17110            panic!("first folded node should be a rule");
17111        };
17112        assert_eq!(rule_index, 1);
17113        assert_eq!(invoking_state, -1);
17114        assert_eq!(start_index, 0);
17115        assert_eq!(stop_index, Some(0));
17116        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
17117        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
17118
17119        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
17120        assert_eq!(
17121            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17122            (4, 3, 1)
17123        );
17124        assert_eq!(
17125            (stats.total_links, stats.live_links, stats.dead_links),
17126            (9, 3, 6)
17127        );
17128    }
17129
17130    #[test]
17131    fn recognition_arena_reports_live_dead_and_retained_capacity() {
17132        let mut arena = RecognitionArena::default();
17133        let token = arena.push_node(ArenaRecognizedNode::Token {
17134            token: TokenId::try_from(0).expect("test token ID"),
17135        });
17136        let extra = arena.push_extra(RecognitionExtra::MissingToken {
17137            token_type: 2,
17138            at_index: 1,
17139            text: "<missing X>".to_owned(),
17140        });
17141        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
17142        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
17143            token: TokenId::try_from(1).expect("test token ID"),
17144        });
17145        let mut live = NodeSeqId::EMPTY;
17146        live = arena.prepend(live, missing);
17147        live = arena.prepend(live, token);
17148        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
17149        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17150            line: 1,
17151            column: 0,
17152            message: "missing X".to_owned(),
17153        }]);
17154        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17155            line: 1,
17156            column: 1,
17157            message: "discarded".to_owned(),
17158        }]);
17159        let deferred_children = arena.deferred_fragment(live);
17160        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
17161            rule_index: 0,
17162            invoking_state: -1,
17163            start_index: 0,
17164            stop_index: Some(1),
17165            deferred_children,
17166            children: NodeSeqId::EMPTY,
17167        });
17168
17169        let stats = arena.stats(live, live_diagnostics);
17170
17171        assert_eq!(
17172            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17173            (3, 2, 1)
17174        );
17175        assert_eq!(
17176            (stats.total_links, stats.live_links, stats.dead_links),
17177            (5, 3, 2)
17178        );
17179        assert_eq!(
17180            (stats.total_extras, stats.live_extras, stats.dead_extras),
17181            (3, 2, 1)
17182        );
17183        assert!(size_of::<SeqLink>() <= 8);
17184        assert!(size_of::<DiagnosticLink>() <= 8);
17185        assert!(size_of::<FastDeferredNode>() <= 12);
17186        assert!(size_of::<FastDeferredRule>() <= 28);
17187        assert!(size_of::<FastRecognizeOutcome>() <= 24);
17188        let capacities = (
17189            stats.node_capacity,
17190            stats.link_capacity,
17191            stats.extra_capacity,
17192        );
17193        let deferred_capacities = (
17194            arena.deferred_nodes.capacity(),
17195            arena.deferred_rules.capacity(),
17196        );
17197
17198        arena.reset();
17199        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
17200        assert_eq!(
17201            (reset.total_nodes, reset.total_links, reset.total_extras),
17202            (0, 0, 0)
17203        );
17204        assert_eq!(
17205            (
17206                reset.node_capacity,
17207                reset.link_capacity,
17208                reset.extra_capacity,
17209            ),
17210            capacities
17211        );
17212        assert!(arena.deferred_nodes.is_empty());
17213        assert!(arena.deferred_rules.is_empty());
17214        assert_eq!(
17215            (
17216                arena.deferred_nodes.capacity(),
17217                arena.deferred_rules.capacity(),
17218            ),
17219            deferred_capacities
17220        );
17221    }
17222
17223    #[test]
17224    fn parser_computes_recognition_arena_stats_on_demand() {
17225        let mut parser = mini_parser(Vec::new());
17226        let live = parser
17227            .recognition_arena
17228            .push_node(ArenaRecognizedNode::Token {
17229                token: TokenId::try_from(0).expect("test token ID"),
17230            });
17231        let discarded = parser
17232            .recognition_arena
17233            .push_node(ArenaRecognizedNode::ErrorToken {
17234                token: TokenId::try_from(1).expect("test token ID"),
17235            });
17236        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
17237        let _discarded_root = parser
17238            .recognition_arena
17239            .prepend(NodeSeqId::EMPTY, discarded);
17240        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
17241
17242        let stats = parser.recognition_arena_stats();
17243
17244        assert_eq!(
17245            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17246            (2, 1, 1)
17247        );
17248        assert_eq!(
17249            (stats.total_links, stats.live_links, stats.dead_links),
17250            (2, 1, 1)
17251        );
17252    }
17253
17254    #[test]
17255    fn recognition_arena_drops_capacity_above_retention_limit() {
17256        let mut storage = Vec::<u8>::with_capacity(4);
17257        storage.extend([1, 2, 3]);
17258
17259        reset_arena_vec(&mut storage, 3);
17260
17261        assert!(storage.is_empty());
17262        assert_eq!(storage.capacity(), 0);
17263    }
17264
17265    #[test]
17266    fn recognition_arena_concatenates_diagnostics_in_source_order() {
17267        let mut arena = RecognitionArena::default();
17268        let prefix = arena.diagnostic_sequence([
17269            ParserDiagnostic {
17270                line: 1,
17271                column: 0,
17272                message: "first".to_owned(),
17273            },
17274            ParserDiagnostic {
17275                line: 1,
17276                column: 1,
17277                message: "second".to_owned(),
17278            },
17279        ]);
17280        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
17281            line: 1,
17282            column: 2,
17283            message: "third".to_owned(),
17284        }]);
17285        let extras_before = arena.extras.len();
17286
17287        let combined = arena.concat_diagnostics(prefix, suffix);
17288        let messages = arena
17289            .diagnostics(combined)
17290            .map(|diagnostic| diagnostic.message.as_str())
17291            .collect::<Vec<_>>();
17292
17293        assert_eq!(messages, ["first", "second", "third"]);
17294        assert_eq!(arena.extras.len(), extras_before);
17295    }
17296
17297    #[test]
17298    fn outcome_ties_keep_later_non_recursive_alternative() {
17299        let arena = RecognitionArena::default();
17300        let first = RecognizeOutcome {
17301            index: 1,
17302            consumed_eof: false,
17303            alt_number: 0,
17304            member_values: BTreeMap::new(),
17305            return_values: BTreeMap::new(),
17306            diagnostics: DiagnosticSeqId::EMPTY,
17307            decisions: Vec::new(),
17308            actions: vec![ParserAction::new(1, 0, 0, None)],
17309            nodes: NodeSeqId::EMPTY,
17310        };
17311        let second = RecognizeOutcome {
17312            actions: vec![ParserAction::new(2, 0, 0, None)],
17313            ..first.clone()
17314        };
17315
17316        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17317            .expect("one outcome should be selected");
17318        assert_eq!(selected.actions[0].source_state(), 2);
17319    }
17320
17321    #[test]
17322    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
17323        let arena = RecognitionArena::default();
17324        let first = RecognizeOutcome {
17325            index: 1,
17326            consumed_eof: false,
17327            alt_number: 0,
17328            member_values: BTreeMap::new(),
17329            return_values: BTreeMap::new(),
17330            diagnostics: DiagnosticSeqId::EMPTY,
17331            decisions: Vec::new(),
17332            actions: vec![ParserAction::new(1, 0, 0, None)],
17333            nodes: NodeSeqId::EMPTY,
17334        };
17335        let second = RecognizeOutcome {
17336            actions: vec![
17337                ParserAction::new(2, 0, 0, None),
17338                ParserAction::new(3, 0, 0, None),
17339            ],
17340            ..first.clone()
17341        };
17342
17343        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
17344            .expect("one outcome should be selected");
17345        assert_eq!(selected.actions.len(), 2);
17346    }
17347
17348    #[test]
17349    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
17350        let arena = RecognitionArena::default();
17351        let first = RecognizeOutcome {
17352            index: 7,
17353            consumed_eof: false,
17354            alt_number: 0,
17355            member_values: BTreeMap::new(),
17356            return_values: BTreeMap::new(),
17357            diagnostics: DiagnosticSeqId::EMPTY,
17358            decisions: vec![1, 0],
17359            actions: vec![
17360                ParserAction::new(23, 2, 2, Some(4)),
17361                ParserAction::new(23, 2, 0, Some(6)),
17362            ],
17363            nodes: NodeSeqId::EMPTY,
17364        };
17365        let second = RecognizeOutcome {
17366            decisions: vec![0, 1],
17367            actions: vec![
17368                ParserAction::new(23, 2, 2, Some(6)),
17369                ParserAction::new(23, 2, 0, Some(6)),
17370            ],
17371            ..first.clone()
17372        };
17373
17374        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17375            .expect("one outcome should be selected");
17376        assert_eq!(selected.actions[0].stop_index(), Some(6));
17377    }
17378
17379    #[test]
17380    fn outcome_ties_keep_first_recursive_tree_shape() {
17381        let mut arena = RecognitionArena::default();
17382        let token = arena.push_node(ArenaRecognizedNode::Token {
17383            token: TokenId::try_from(0).expect("test token ID"),
17384        });
17385        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
17386        let inner = arena.push_node(ArenaRecognizedNode::Rule {
17387            rule_index: 1,
17388            invoking_state: -1,
17389            alt_number: 0,
17390            start_index: 0,
17391            stop_index: Some(0),
17392            return_values: None,
17393            children: token_children,
17394        });
17395        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
17396        let outer = arena.push_node(ArenaRecognizedNode::Rule {
17397            rule_index: 1,
17398            invoking_state: -1,
17399            alt_number: 0,
17400            start_index: 0,
17401            stop_index: Some(0),
17402            return_values: None,
17403            children: inner_children,
17404        });
17405        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
17406        let first = RecognizeOutcome {
17407            index: 1,
17408            consumed_eof: false,
17409            alt_number: 0,
17410            member_values: BTreeMap::new(),
17411            return_values: BTreeMap::new(),
17412            diagnostics: DiagnosticSeqId::EMPTY,
17413            decisions: Vec::new(),
17414            actions: vec![ParserAction::new(1, 0, 0, None)],
17415            nodes: recursive_nodes,
17416        };
17417        let second = RecognizeOutcome {
17418            index: 1,
17419            consumed_eof: false,
17420            alt_number: 0,
17421            member_values: BTreeMap::new(),
17422            return_values: BTreeMap::new(),
17423            diagnostics: DiagnosticSeqId::EMPTY,
17424            decisions: Vec::new(),
17425            actions: vec![ParserAction::new(2, 0, 0, None)],
17426            nodes: recursive_nodes,
17427        };
17428
17429        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17430            .expect("one outcome should be selected");
17431        assert_eq!(selected.actions[0].source_state(), 1);
17432    }
17433
17434    #[test]
17435    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
17436        let mut arena = RecognitionArena::default();
17437        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17438            line: 1,
17439            column: 3,
17440            message: "missing 'Y' at '<EOF>'".to_owned(),
17441        }]);
17442        let first_alt = RecognizeOutcome {
17443            index: 2,
17444            consumed_eof: true,
17445            alt_number: 0,
17446            member_values: BTreeMap::new(),
17447            return_values: BTreeMap::new(),
17448            diagnostics: recovered_diagnostics,
17449            decisions: vec![0],
17450            actions: vec![ParserAction::new(1, 0, 0, None)],
17451            nodes: NodeSeqId::EMPTY,
17452        };
17453        let second_alt = RecognizeOutcome {
17454            diagnostics: DiagnosticSeqId::EMPTY,
17455            decisions: vec![1],
17456            actions: vec![ParserAction::new(2, 0, 0, None)],
17457            ..first_alt.clone()
17458        };
17459
17460        let selected = select_best_outcome(
17461            [second_alt, first_alt].into_iter(),
17462            PredictionMode::Sll,
17463            &arena,
17464        )
17465        .expect("one outcome should be selected");
17466        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
17467        assert_eq!(selected.decisions, [0]);
17468    }
17469}