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 nested_rule_chain_atn(depth: usize) -> Atn {
12421        assert!(depth > 0);
12422        let mut atn = ParserAtnBuilder::new(1);
12423        let mut starts = Vec::with_capacity(depth);
12424        let mut stops = Vec::with_capacity(depth);
12425        for rule_index in 0..depth {
12426            starts.push(
12427                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
12428                    .expect("rule start")
12429                    .index(),
12430            );
12431        }
12432        for rule_index in 0..depth {
12433            stops.push(
12434                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
12435                    .expect("rule stop")
12436                    .index(),
12437            );
12438        }
12439        atn.set_rule_to_start_state(starts.clone())
12440            .expect("rule start states");
12441        atn.set_rule_to_stop_state(stops.clone())
12442            .expect("rule stop states");
12443        for rule_index in 0..depth - 1 {
12444            atn.add_transition(
12445                starts[rule_index],
12446                ParserTransitionSpec::Rule {
12447                    target: starts[rule_index + 1],
12448                    rule_index: rule_index + 1,
12449                    follow_state: stops[rule_index],
12450                    precedence: 0,
12451                },
12452            )
12453            .expect("nested rule transition");
12454        }
12455        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
12456        atn.add_transition(
12457            starts[depth - 1],
12458            ParserTransitionSpec::Set {
12459                target: stops[depth - 1],
12460                set: token_set,
12461            },
12462        )
12463        .expect("terminal set transition");
12464        finish_atn(atn)
12465    }
12466
12467    fn ordinary_star_loop_atn() -> Atn {
12468        let mut atn = ParserAtnBuilder::new(2);
12469        for (state_number, kind, rule_index) in [
12470            (0, AtnStateKind::RuleStart, 0),
12471            (1, AtnStateKind::StarLoopEntry, 0),
12472            (2, AtnStateKind::Basic, 0),
12473            (3, AtnStateKind::StarLoopBack, 0),
12474            (4, AtnStateKind::LoopEnd, 0),
12475            (5, AtnStateKind::Basic, 0),
12476            (6, AtnStateKind::RuleStop, 0),
12477            (7, AtnStateKind::RuleStart, 1),
12478            (8, AtnStateKind::Basic, 1),
12479            (9, AtnStateKind::RuleStop, 1),
12480        ] {
12481            assert_eq!(
12482                atn.add_state(kind, Some(rule_index))
12483                    .expect("state")
12484                    .index(),
12485                state_number
12486            );
12487        }
12488        atn.set_rule_to_start_state(vec![0, 7])
12489            .expect("rule start states");
12490        atn.set_rule_to_stop_state(vec![6, 9])
12491            .expect("rule stop states");
12492        atn.add_decision_state(1).expect("decision state");
12493        atn.set_loop_back_state(4, 3).expect("loop back state");
12494        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12495            .expect("transition");
12496        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12497            .expect("transition");
12498        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
12499            .expect("transition");
12500        atn.add_transition(
12501            2,
12502            ParserTransitionSpec::Rule {
12503                target: 7,
12504                rule_index: 1,
12505                follow_state: 3,
12506                precedence: 0,
12507            },
12508        )
12509        .expect("transition");
12510        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
12511            .expect("transition");
12512        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12513            .expect("transition");
12514        atn.add_transition(
12515            5,
12516            ParserTransitionSpec::Atom {
12517                target: 6,
12518                label: TOKEN_EOF,
12519            },
12520        )
12521        .expect("transition");
12522        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12523            .expect("transition");
12524        atn.add_transition(
12525            8,
12526            ParserTransitionSpec::Atom {
12527                target: 9,
12528                label: 1,
12529            },
12530        )
12531        .expect("transition");
12532        finish_atn(atn)
12533    }
12534
12535    /// ATN for `s : (X | X X)* EOF`.
12536    fn ambiguous_ordinary_star_loop_atn() -> Atn {
12537        let mut atn = ParserAtnBuilder::new(1);
12538        for (state_number, kind) in [
12539            (0, AtnStateKind::RuleStart),
12540            (1, AtnStateKind::StarLoopEntry),
12541            (2, AtnStateKind::StarBlockStart),
12542            (3, AtnStateKind::Basic),
12543            (4, AtnStateKind::BlockEnd),
12544            (5, AtnStateKind::StarLoopBack),
12545            (6, AtnStateKind::LoopEnd),
12546            (7, AtnStateKind::Basic),
12547            (8, AtnStateKind::RuleStop),
12548        ] {
12549            assert_eq!(
12550                atn.add_state(kind, Some(0)).expect("state").index(),
12551                state_number
12552            );
12553        }
12554        atn.set_rule_to_start_state(vec![0])
12555            .expect("rule start states");
12556        atn.set_rule_to_stop_state(vec![8])
12557            .expect("rule stop states");
12558        atn.set_end_state(2, 4).expect("block end state");
12559        atn.set_loop_back_state(6, 5).expect("loop back state");
12560        atn.add_decision_state(1).expect("decision state");
12561        atn.add_decision_state(2).expect("decision state");
12562        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12563            .expect("transition");
12564        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12565            .expect("transition");
12566        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
12567            .expect("transition");
12568        atn.add_transition(
12569            2,
12570            ParserTransitionSpec::Atom {
12571                target: 4,
12572                label: 1,
12573            },
12574        )
12575        .expect("transition");
12576        atn.add_transition(
12577            2,
12578            ParserTransitionSpec::Atom {
12579                target: 3,
12580                label: 1,
12581            },
12582        )
12583        .expect("transition");
12584        atn.add_transition(
12585            3,
12586            ParserTransitionSpec::Atom {
12587                target: 4,
12588                label: 1,
12589            },
12590        )
12591        .expect("transition");
12592        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12593            .expect("transition");
12594        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
12595            .expect("transition");
12596        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12597            .expect("transition");
12598        atn.add_transition(
12599            7,
12600            ParserTransitionSpec::Atom {
12601                target: 8,
12602                label: TOKEN_EOF,
12603            },
12604        )
12605        .expect("transition");
12606        finish_atn(atn)
12607    }
12608
12609    fn ordinary_plus_loop_atn() -> Atn {
12610        let mut atn = ParserAtnBuilder::new(2);
12611        for (state_number, kind, rule_index) in [
12612            (0, AtnStateKind::RuleStart, 0),
12613            (1, AtnStateKind::Basic, 0),
12614            (2, AtnStateKind::PlusLoopBack, 0),
12615            (3, AtnStateKind::LoopEnd, 0),
12616            (4, AtnStateKind::Basic, 0),
12617            (5, AtnStateKind::RuleStop, 0),
12618            (6, AtnStateKind::RuleStart, 1),
12619            (7, AtnStateKind::Basic, 1),
12620            (8, AtnStateKind::RuleStop, 1),
12621        ] {
12622            assert_eq!(
12623                atn.add_state(kind, Some(rule_index))
12624                    .expect("state")
12625                    .index(),
12626                state_number
12627            );
12628        }
12629        atn.set_rule_to_start_state(vec![0, 6])
12630            .expect("rule start states");
12631        atn.set_rule_to_stop_state(vec![5, 8])
12632            .expect("rule stop states");
12633        atn.add_decision_state(2).expect("decision state");
12634        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
12635            .expect("transition");
12636        atn.add_transition(
12637            1,
12638            ParserTransitionSpec::Rule {
12639                target: 6,
12640                rule_index: 1,
12641                follow_state: 2,
12642                precedence: 0,
12643            },
12644        )
12645        .expect("transition");
12646        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
12647            .expect("transition");
12648        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12649            .expect("transition");
12650        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
12651            .expect("transition");
12652        atn.add_transition(
12653            4,
12654            ParserTransitionSpec::Atom {
12655                target: 5,
12656                label: TOKEN_EOF,
12657            },
12658        )
12659        .expect("transition");
12660        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
12661            .expect("transition");
12662        atn.add_transition(
12663            7,
12664            ParserTransitionSpec::Atom {
12665                target: 8,
12666                label: 1,
12667            },
12668        )
12669        .expect("transition");
12670        finish_atn(atn)
12671    }
12672
12673    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
12674        let mut tokens = (0..count)
12675            .map(|_| TestToken::new(1).with_text("x"))
12676            .collect::<Vec<_>>();
12677        tokens.push(TestToken::eof("parser-test", count, 1, count));
12678        tokens
12679    }
12680
12681    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
12682        let mut atn = ParserAtnBuilder::new(2);
12683        assert_eq!(
12684            atn.add_state(AtnStateKind::RuleStart, Some(0))
12685                .expect("state")
12686                .index(),
12687            0
12688        );
12689        assert_eq!(
12690            atn.add_state(AtnStateKind::Basic, Some(0))
12691                .expect("state")
12692                .index(),
12693            1
12694        );
12695        assert_eq!(
12696            atn.add_state(AtnStateKind::Basic, Some(0))
12697                .expect("state")
12698                .index(),
12699            2
12700        );
12701        assert_eq!(
12702            atn.add_state(AtnStateKind::RuleStart, Some(1))
12703                .expect("state")
12704                .index(),
12705            3
12706        );
12707        atn.set_left_recursive_rule(3)
12708            .expect("left-recursive rule start");
12709        assert_eq!(
12710            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
12711                .expect("state")
12712                .index(),
12713            4
12714        );
12715        atn.set_precedence_rule_decision(4)
12716            .expect("precedence decision");
12717        assert_eq!(
12718            atn.add_state(AtnStateKind::Basic, Some(1))
12719                .expect("state")
12720                .index(),
12721            5
12722        );
12723        assert_eq!(
12724            atn.add_state(AtnStateKind::Basic, Some(1))
12725                .expect("state")
12726                .index(),
12727            6
12728        );
12729        assert_eq!(
12730            atn.add_state(AtnStateKind::LoopEnd, Some(1))
12731                .expect("state")
12732                .index(),
12733            7
12734        );
12735        assert_eq!(
12736            atn.add_state(AtnStateKind::RuleStop, Some(1))
12737                .expect("state")
12738                .index(),
12739            8
12740        );
12741        assert_eq!(
12742            atn.add_state(AtnStateKind::RuleStop, Some(0))
12743                .expect("state")
12744                .index(),
12745            9
12746        );
12747        atn.set_rule_to_start_state(vec![0, 3])
12748            .expect("rule start states");
12749        atn.set_rule_to_stop_state(vec![9, 8])
12750            .expect("rule stop states");
12751        atn.add_transition(
12752            1,
12753            ParserTransitionSpec::Rule {
12754                target: 3,
12755                rule_index: 1,
12756                follow_state: 2,
12757                precedence: 0,
12758            },
12759        )
12760        .expect("transition");
12761        atn.add_transition(
12762            2,
12763            ParserTransitionSpec::Atom {
12764                target: 9,
12765                label: caller_symbol,
12766            },
12767        )
12768        .expect("transition");
12769        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
12770            .expect("transition");
12771        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
12772            .expect("transition");
12773        atn.add_transition(
12774            5,
12775            ParserTransitionSpec::Precedence {
12776                target: 6,
12777                precedence: 1,
12778            },
12779        )
12780        .expect("transition");
12781        atn.add_transition(
12782            6,
12783            ParserTransitionSpec::Atom {
12784                target: 4,
12785                label: 1,
12786            },
12787        )
12788        .expect("transition");
12789        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
12790            .expect("transition");
12791        finish_atn(atn)
12792    }
12793
12794    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
12795        let mut parser = mini_parser(vec![
12796            TestToken::new(symbol).with_text("lookahead"),
12797            TestToken::eof("parser-test", 1, 1, 1),
12798        ]);
12799        parser.rule_context_stack = vec![
12800            RuleContextFrame {
12801                rule_index: 0,
12802                invoking_state: -1,
12803            },
12804            RuleContextFrame {
12805                rule_index: 1,
12806                invoking_state: 1,
12807            },
12808        ];
12809        parser
12810    }
12811
12812    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
12813        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
12814        //   prec 2: token 1, token 1  (shift `>>`)
12815        //   prec 1: token 1           (relational `>`)
12816        let mut atn = ParserAtnBuilder::new(1);
12817        for (state, kind, rule) in [
12818            (0, AtnStateKind::RuleStart, 0),
12819            (1, AtnStateKind::StarLoopEntry, 0),
12820            (2, AtnStateKind::Basic, 0), // ops hub
12821            (3, AtnStateKind::Basic, 0), // shift prec
12822            (4, AtnStateKind::Basic, 0), // shift first >
12823            (5, AtnStateKind::Basic, 0), // shift second >
12824            (6, AtnStateKind::Basic, 0), // rel prec
12825            (7, AtnStateKind::Basic, 0), // rel >
12826            (8, AtnStateKind::LoopEnd, 0),
12827            (9, AtnStateKind::RuleStop, 0),
12828        ] {
12829            assert_eq!(
12830                atn.add_state(kind, Some(rule)).expect("state").index(),
12831                state
12832            );
12833            if state == 0 {
12834                atn.set_left_recursive_rule(state)
12835                    .expect("left-recursive rule start");
12836            } else if state == 1 {
12837                atn.set_precedence_rule_decision(state)
12838                    .expect("precedence decision");
12839            }
12840        }
12841        atn.set_rule_to_start_state(vec![0])
12842            .expect("rule start states");
12843        atn.set_rule_to_stop_state(vec![9])
12844            .expect("rule stop states");
12845        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12846            .expect("ops");
12847        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12848            .expect("exit");
12849        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12850            .expect("to shift");
12851        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12852            .expect("to rel");
12853        atn.add_transition(
12854            3,
12855            ParserTransitionSpec::Precedence {
12856                target: 4,
12857                precedence: 2,
12858            },
12859        )
12860        .expect("shift prec");
12861        atn.add_transition(
12862            4,
12863            ParserTransitionSpec::Atom {
12864                target: 5,
12865                label: 1,
12866            },
12867        )
12868        .expect("shift first >");
12869        atn.add_transition(
12870            5,
12871            ParserTransitionSpec::Atom {
12872                target: 1,
12873                label: 1,
12874            },
12875        )
12876        .expect("shift second >");
12877        atn.add_transition(
12878            6,
12879            ParserTransitionSpec::Precedence {
12880                target: 7,
12881                precedence: 1,
12882            },
12883        )
12884        .expect("rel prec");
12885        atn.add_transition(
12886            7,
12887            ParserTransitionSpec::Atom {
12888                target: 1,
12889                label: 1,
12890            },
12891        )
12892        .expect("rel >");
12893        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12894            .expect("loop end");
12895        finish_atn(atn)
12896    }
12897
12898    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
12899        let mut atn = ParserAtnBuilder::new(2);
12900        for (state, kind, rule) in [
12901            (0, AtnStateKind::RuleStart, 0),
12902            (1, AtnStateKind::StarLoopEntry, 0),
12903            (2, AtnStateKind::Basic, 0),
12904            (3, AtnStateKind::Basic, 0),
12905            (4, AtnStateKind::Basic, 0),
12906            (5, AtnStateKind::Basic, 0),
12907            (6, AtnStateKind::Basic, 0),
12908            (7, AtnStateKind::Basic, 0),
12909            (8, AtnStateKind::LoopEnd, 0),
12910            (9, AtnStateKind::RuleStop, 0),
12911            (10, AtnStateKind::RuleStart, 1),
12912            (11, AtnStateKind::Basic, 1),
12913            (12, AtnStateKind::RuleStop, 1),
12914        ] {
12915            assert_eq!(
12916                atn.add_state(kind, Some(rule)).expect("state").index(),
12917                state
12918            );
12919            if state == 0 {
12920                atn.set_left_recursive_rule(state)
12921                    .expect("left-recursive rule start");
12922            } else if state == 1 {
12923                atn.set_precedence_rule_decision(state)
12924                    .expect("precedence decision");
12925            }
12926        }
12927        atn.set_rule_to_start_state(vec![0, 10])
12928            .expect("rule start states");
12929        atn.set_rule_to_stop_state(vec![9, 12])
12930            .expect("rule stop states");
12931        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
12932            .expect("ops");
12933        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
12934            .expect("exit");
12935        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
12936            .expect("to shift");
12937        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
12938            .expect("to relational");
12939        atn.add_transition(
12940            3,
12941            ParserTransitionSpec::Precedence {
12942                target: 4,
12943                precedence: 2,
12944            },
12945        )
12946        .expect("shift precedence");
12947        atn.add_transition(
12948            4,
12949            ParserTransitionSpec::Rule {
12950                target: 10,
12951                rule_index: 1,
12952                follow_state: 5,
12953                precedence: 0,
12954            },
12955        )
12956        .expect("first shift token helper");
12957        atn.add_transition(
12958            5,
12959            ParserTransitionSpec::Atom {
12960                target: 1,
12961                label: 1,
12962            },
12963        )
12964        .expect("second shift token");
12965        atn.add_transition(
12966            6,
12967            ParserTransitionSpec::Precedence {
12968                target: 7,
12969                precedence: 1,
12970            },
12971        )
12972        .expect("relational precedence");
12973        atn.add_transition(
12974            7,
12975            ParserTransitionSpec::Atom {
12976                target: 1,
12977                label: 1,
12978            },
12979        )
12980        .expect("relational token");
12981        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
12982            .expect("loop end");
12983        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
12984            .expect("helper entry");
12985        atn.add_transition(
12986            11,
12987            ParserTransitionSpec::Atom {
12988                target: 12,
12989                label: 1,
12990            },
12991        )
12992        .expect("first shift token");
12993        finish_atn(atn)
12994    }
12995
12996    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
12997        let mut atn = ParserAtnBuilder::new(1);
12998        for (state, kind) in [
12999            (0, AtnStateKind::RuleStart),
13000            (1, AtnStateKind::StarLoopEntry),
13001            (2, AtnStateKind::Basic),
13002            (3, AtnStateKind::Basic),
13003            (4, AtnStateKind::Basic),
13004            (5, AtnStateKind::Basic),
13005            (6, AtnStateKind::Basic),
13006            (7, AtnStateKind::Basic),
13007            (8, AtnStateKind::Basic),
13008            (9, AtnStateKind::LoopEnd),
13009            (10, AtnStateKind::RuleStop),
13010        ] {
13011            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13012            if state == 0 {
13013                atn.set_left_recursive_rule(state)
13014                    .expect("left-recursive rule start");
13015            } else if state == 1 {
13016                atn.set_precedence_rule_decision(state)
13017                    .expect("precedence decision");
13018            }
13019        }
13020        atn.set_rule_to_start_state(vec![0])
13021            .expect("rule start states");
13022        atn.set_rule_to_stop_state(vec![10])
13023            .expect("rule stop states");
13024        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13025            .expect("ops");
13026        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
13027            .expect("exit");
13028        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13029            .expect("to multi-token operator");
13030        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13031            .expect("to predicate operator");
13032        atn.add_transition(
13033            3,
13034            ParserTransitionSpec::Precedence {
13035                target: 4,
13036                precedence: 2,
13037            },
13038        )
13039        .expect("multi-token precedence");
13040        atn.add_transition(
13041            4,
13042            ParserTransitionSpec::Atom {
13043                target: 5,
13044                label: 1,
13045            },
13046        )
13047        .expect("multi-token first");
13048        atn.add_transition(
13049            5,
13050            ParserTransitionSpec::Atom {
13051                target: 1,
13052                label: 1,
13053            },
13054        )
13055        .expect("multi-token second");
13056        atn.add_transition(
13057            6,
13058            ParserTransitionSpec::Precedence {
13059                target: 7,
13060                precedence: 2,
13061            },
13062        )
13063        .expect("predicate precedence");
13064        atn.add_transition(
13065            7,
13066            ParserTransitionSpec::Predicate {
13067                target: 8,
13068                rule_index: 0,
13069                pred_index: 0,
13070                context_dependent: false,
13071            },
13072        )
13073        .expect("operator predicate");
13074        atn.add_transition(
13075            8,
13076            ParserTransitionSpec::Atom {
13077                target: 1,
13078                label: 1,
13079            },
13080        )
13081        .expect("predicate single token");
13082        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13083            .expect("loop end");
13084        finish_atn(atn)
13085    }
13086
13087    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
13088        let mut atn = ParserAtnBuilder::new(2);
13089        for (state, kind, rule) in [
13090            (0, AtnStateKind::RuleStart, 0),
13091            (1, AtnStateKind::StarLoopEntry, 0),
13092            (2, AtnStateKind::Basic, 0),
13093            (3, AtnStateKind::Basic, 0),
13094            (4, AtnStateKind::Basic, 0),
13095            (5, AtnStateKind::LoopEnd, 0),
13096            (6, AtnStateKind::RuleStop, 0),
13097            (7, AtnStateKind::RuleStart, 1),
13098            (8, AtnStateKind::RuleStop, 1),
13099            (9, AtnStateKind::Basic, 1),
13100        ] {
13101            assert_eq!(
13102                atn.add_state(kind, Some(rule)).expect("state").index(),
13103                state
13104            );
13105            if state == 0 {
13106                atn.set_left_recursive_rule(state)
13107                    .expect("left-recursive rule start");
13108            } else if state == 1 {
13109                atn.set_precedence_rule_decision(state)
13110                    .expect("precedence decision");
13111            }
13112        }
13113        atn.set_rule_to_start_state(vec![0, 7])
13114            .expect("rule start states");
13115        atn.set_rule_to_stop_state(vec![6, 8])
13116            .expect("rule stop states");
13117        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13118            .expect("transition");
13119        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13120            .expect("transition");
13121        atn.add_transition(
13122            2,
13123            ParserTransitionSpec::Precedence {
13124                target: 3,
13125                precedence: 3,
13126            },
13127        )
13128        .expect("transition");
13129        atn.add_transition(
13130            3,
13131            ParserTransitionSpec::Rule {
13132                target: 7,
13133                rule_index: 1,
13134                follow_state: 4,
13135                precedence: 0,
13136            },
13137        )
13138        .expect("transition");
13139        atn.add_transition(
13140            4,
13141            ParserTransitionSpec::Atom {
13142                target: 1,
13143                label: 1,
13144            },
13145        )
13146        .expect("transition");
13147        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13148            .expect("transition");
13149        atn.add_transition(
13150            7,
13151            ParserTransitionSpec::Precedence {
13152                target: 9,
13153                precedence: 1,
13154            },
13155        )
13156        .expect("transition");
13157        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
13158            .expect("transition");
13159        finish_atn(atn)
13160    }
13161
13162    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
13163        let mut atn = ParserAtnBuilder::new(2);
13164        for (state, kind) in [
13165            (0, AtnStateKind::RuleStart),
13166            (1, AtnStateKind::StarLoopEntry),
13167            (2, AtnStateKind::Basic),
13168            (3, AtnStateKind::Basic),
13169            (4, AtnStateKind::Basic),
13170            (5, AtnStateKind::LoopEnd),
13171            (6, AtnStateKind::RuleStop),
13172        ] {
13173            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13174            if state == 0 {
13175                atn.set_left_recursive_rule(state)
13176                    .expect("left-recursive rule start");
13177            } else if state == 1 {
13178                atn.set_precedence_rule_decision(state)
13179                    .expect("precedence decision");
13180            }
13181        }
13182        atn.set_rule_to_start_state(vec![0])
13183            .expect("rule start states");
13184        atn.set_rule_to_stop_state(vec![6])
13185            .expect("rule stop states");
13186        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13187            .expect("transition");
13188        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
13189            .expect("transition");
13190        atn.add_transition(
13191            2,
13192            ParserTransitionSpec::Precedence {
13193                target: 3,
13194                precedence: 1,
13195            },
13196        )
13197        .expect("transition");
13198        atn.add_transition(
13199            3,
13200            ParserTransitionSpec::Predicate {
13201                target: 4,
13202                rule_index: 0,
13203                pred_index: 0,
13204                context_dependent: false,
13205            },
13206        )
13207        .expect("transition");
13208        atn.add_transition(
13209            4,
13210            ParserTransitionSpec::Atom {
13211                target: 1,
13212                label: 1,
13213            },
13214        )
13215        .expect("transition");
13216        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13217            .expect("transition");
13218        finish_atn(atn)
13219    }
13220
13221    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
13222        let mut atn = ParserAtnBuilder::new(2);
13223        for (state, kind, rule) in [
13224            (0, AtnStateKind::RuleStart, 0),
13225            (1, AtnStateKind::Basic, 0),
13226            (2, AtnStateKind::Basic, 0),
13227            (3, AtnStateKind::Basic, 0),
13228            (4, AtnStateKind::RuleStop, 0),
13229            (5, AtnStateKind::RuleStart, 1),
13230            (6, AtnStateKind::StarLoopEntry, 1),
13231            (7, AtnStateKind::Basic, 1),
13232            (8, AtnStateKind::Basic, 1),
13233            (9, AtnStateKind::LoopEnd, 1),
13234            (10, AtnStateKind::RuleStop, 1),
13235            (11, AtnStateKind::RuleStart, 2),
13236            (12, AtnStateKind::RuleStop, 2),
13237        ] {
13238            assert_eq!(
13239                atn.add_state(kind, Some(rule)).expect("state").index(),
13240                state
13241            );
13242            if state == 5 {
13243                atn.set_left_recursive_rule(state)
13244                    .expect("left-recursive rule start");
13245            } else if state == 6 {
13246                atn.set_precedence_rule_decision(state)
13247                    .expect("precedence decision");
13248            }
13249        }
13250        atn.set_rule_to_start_state(vec![0, 5, 11])
13251            .expect("rule start states");
13252        atn.set_rule_to_stop_state(vec![4, 10, 12])
13253            .expect("rule stop states");
13254        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13255            .expect("transition");
13256        atn.add_transition(
13257            1,
13258            ParserTransitionSpec::Rule {
13259                target: 5,
13260                rule_index: 1,
13261                follow_state: 2,
13262                precedence: 0,
13263            },
13264        )
13265        .expect("transition");
13266        atn.add_transition(
13267            2,
13268            ParserTransitionSpec::Rule {
13269                target: 11,
13270                rule_index: 2,
13271                follow_state: 3,
13272                precedence: 0,
13273            },
13274        )
13275        .expect("transition");
13276        atn.add_transition(
13277            3,
13278            ParserTransitionSpec::Atom {
13279                target: 4,
13280                label: caller_symbol,
13281            },
13282        )
13283        .expect("transition");
13284        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13285            .expect("transition");
13286        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
13287            .expect("transition");
13288        atn.add_transition(
13289            7,
13290            ParserTransitionSpec::Precedence {
13291                target: 8,
13292                precedence: 1,
13293            },
13294        )
13295        .expect("transition");
13296        atn.add_transition(
13297            8,
13298            ParserTransitionSpec::Atom {
13299                target: 6,
13300                label: 1,
13301            },
13302        )
13303        .expect("transition");
13304        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13305            .expect("transition");
13306        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
13307            .expect("transition");
13308        finish_atn(atn)
13309    }
13310
13311    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
13312        let mut atn = ParserAtnBuilder::new(2);
13313        for (state, kind, rule) in [
13314            (0, AtnStateKind::RuleStart, 0),
13315            (1, AtnStateKind::Basic, 0),
13316            (2, AtnStateKind::Basic, 0),
13317            (3, AtnStateKind::RuleStop, 0),
13318            (4, AtnStateKind::RuleStart, 1),
13319            (5, AtnStateKind::Basic, 1),
13320            (6, AtnStateKind::Basic, 1),
13321            (7, AtnStateKind::RuleStop, 1),
13322            (8, AtnStateKind::RuleStart, 2),
13323            (9, AtnStateKind::StarLoopEntry, 2),
13324            (10, AtnStateKind::Basic, 2),
13325            (11, AtnStateKind::Basic, 2),
13326            (12, AtnStateKind::LoopEnd, 2),
13327            (13, AtnStateKind::RuleStop, 2),
13328        ] {
13329            assert_eq!(
13330                atn.add_state(kind, Some(rule)).expect("state").index(),
13331                state
13332            );
13333            if state == 8 {
13334                atn.set_left_recursive_rule(state)
13335                    .expect("left-recursive rule start");
13336            } else if state == 9 {
13337                atn.set_precedence_rule_decision(state)
13338                    .expect("precedence decision");
13339            }
13340        }
13341        atn.set_rule_to_start_state(vec![0, 4, 8])
13342            .expect("rule start states");
13343        atn.set_rule_to_stop_state(vec![3, 7, 13])
13344            .expect("rule stop states");
13345        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13346            .expect("transition");
13347        atn.add_transition(
13348            1,
13349            ParserTransitionSpec::Rule {
13350                target: 4,
13351                rule_index: 1,
13352                follow_state: 2,
13353                precedence: 0,
13354            },
13355        )
13356        .expect("transition");
13357        atn.add_transition(
13358            2,
13359            ParserTransitionSpec::Atom {
13360                target: 3,
13361                label: caller_symbol,
13362            },
13363        )
13364        .expect("transition");
13365        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13366            .expect("transition");
13367        atn.add_transition(
13368            5,
13369            ParserTransitionSpec::Rule {
13370                target: 8,
13371                rule_index: 2,
13372                follow_state: 6,
13373                precedence: 0,
13374            },
13375        )
13376        .expect("transition");
13377        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13378            .expect("transition");
13379        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
13380            .expect("transition");
13381        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
13382            .expect("transition");
13383        atn.add_transition(
13384            10,
13385            ParserTransitionSpec::Precedence {
13386                target: 11,
13387                precedence: 1,
13388            },
13389        )
13390        .expect("transition");
13391        atn.add_transition(
13392            11,
13393            ParserTransitionSpec::Atom {
13394                target: 9,
13395                label: 1,
13396            },
13397        )
13398        .expect("transition");
13399        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
13400            .expect("transition");
13401        finish_atn(atn)
13402    }
13403
13404    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
13405        let mut atn = ParserAtnBuilder::new(2);
13406        for (state, kind, rule) in [
13407            (0, AtnStateKind::RuleStart, 0),
13408            (1, AtnStateKind::Basic, 0),
13409            (2, AtnStateKind::Basic, 0),
13410            (3, AtnStateKind::RuleStop, 0),
13411            (4, AtnStateKind::RuleStart, 1),
13412            (5, AtnStateKind::StarLoopEntry, 1),
13413            (6, AtnStateKind::Basic, 1),
13414            (7, AtnStateKind::Basic, 1),
13415            (8, AtnStateKind::Basic, 1),
13416            (9, AtnStateKind::Basic, 1),
13417            (10, AtnStateKind::LoopEnd, 1),
13418            (11, AtnStateKind::RuleStop, 1),
13419        ] {
13420            assert_eq!(
13421                atn.add_state(kind, Some(rule)).expect("state").index(),
13422                state
13423            );
13424            if state == 4 {
13425                atn.set_left_recursive_rule(state)
13426                    .expect("left-recursive rule start");
13427            } else if state == 5 {
13428                atn.set_precedence_rule_decision(state)
13429                    .expect("precedence decision");
13430            }
13431        }
13432        atn.set_rule_to_start_state(vec![0, 4])
13433            .expect("rule start states");
13434        atn.set_rule_to_stop_state(vec![3, 11])
13435            .expect("rule stop states");
13436        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13437            .expect("transition");
13438        atn.add_transition(
13439            1,
13440            ParserTransitionSpec::Rule {
13441                target: 4,
13442                rule_index: 1,
13443                follow_state: 2,
13444                precedence: 0,
13445            },
13446        )
13447        .expect("transition");
13448        atn.add_transition(
13449            2,
13450            ParserTransitionSpec::Atom {
13451                target: 3,
13452                label: caller_symbol,
13453            },
13454        )
13455        .expect("transition");
13456        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
13457            .expect("transition");
13458        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
13459            .expect("transition");
13460        atn.add_transition(
13461            6,
13462            ParserTransitionSpec::Precedence {
13463                target: 7,
13464                precedence: 1,
13465            },
13466        )
13467        .expect("transition");
13468        atn.add_transition(
13469            7,
13470            ParserTransitionSpec::Atom {
13471                target: 8,
13472                label: 1,
13473            },
13474        )
13475        .expect("transition");
13476        atn.add_transition(
13477            8,
13478            ParserTransitionSpec::Rule {
13479                target: 4,
13480                rule_index: 1,
13481                follow_state: 9,
13482                precedence: 2,
13483            },
13484        )
13485        .expect("transition");
13486        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
13487            .expect("transition");
13488        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13489            .expect("transition");
13490        finish_atn(atn)
13491    }
13492
13493    #[test]
13494    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
13495        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
13496        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
13497
13498        let mut overlapping = parser_inside_left_recursive_callee(1);
13499        assert_eq!(
13500            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
13501            None
13502        );
13503
13504        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
13505        assert_eq!(
13506            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13507            Some(true)
13508        );
13509
13510        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
13511        assert_eq!(
13512            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13513            Some(false)
13514        );
13515
13516        assert_eq!(
13517            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
13518            Some(true),
13519            "overlap results must not leak across ATNs"
13520        );
13521    }
13522
13523    #[test]
13524    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
13525        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
13526        let mut parser = mini_parser(vec![
13527            TestToken::new(1).with_text("operator"),
13528            TestToken::eof("parser-test", 1, 1, 1),
13529        ]);
13530        parser.rule_context_stack = vec![RuleContextFrame {
13531            rule_index: 0,
13532            invoking_state: -1,
13533        }];
13534
13535        assert_eq!(
13536            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13537            Some(true)
13538        );
13539        assert_eq!(
13540            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13541            Some(true),
13542            "cached operator lookahead must preserve the nullable prefix return path"
13543        );
13544        assert_eq!(
13545            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13546            Some(true),
13547            "the nullable child must use its rule-call precedence, not the caller precedence"
13548        );
13549    }
13550
13551    #[test]
13552    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
13553        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
13554        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
13555        // must defer so StarLoopEntry adaptive predict can exit when the second
13556        // `>` is absent (as in `a < b > c`).
13557        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
13558        let mut parser = mini_parser(vec![
13559            TestToken::new(1).with_text(">"),
13560            TestToken::new(2).with_text("id"),
13561            TestToken::eof("parser-test", 1, 1, 1),
13562        ]);
13563        parser.rule_context_stack = vec![RuleContextFrame {
13564            rule_index: 0,
13565            invoking_state: -1,
13566        }];
13567
13568        assert_eq!(
13569            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13570            Some(true),
13571            "at low precedence relational `>` is a single-token operator"
13572        );
13573        assert_eq!(
13574            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
13575            Some(true),
13576            "relational remains single-token at its own precedence"
13577        );
13578        assert_eq!(
13579            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13580            None,
13581            "at shift precedence, bare `>` must not force enter"
13582        );
13583    }
13584
13585    #[test]
13586    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
13587        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
13588        let mut parser = mini_parser(vec![
13589            TestToken::new(1).with_text(">"),
13590            TestToken::new(2).with_text("id"),
13591            TestToken::eof("parser-test", 1, 1, 1),
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            Some(true),
13601            "the direct relational alternative remains a one-token operator"
13602        );
13603        assert_eq!(
13604            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13605            None,
13606            "a token matched in the helper rule must return to the second shift token"
13607        );
13608    }
13609
13610    #[test]
13611    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
13612        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
13613        let mut parser = mini_parser(vec![
13614            TestToken::new(1).with_text(">"),
13615            TestToken::new(2).with_text("id"),
13616            TestToken::eof("parser-test", 1, 1, 1),
13617        ]);
13618        parser.rule_context_stack = vec![RuleContextFrame {
13619            rule_index: 0,
13620            invoking_state: -1,
13621        }];
13622
13623        assert_eq!(
13624            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
13625            None,
13626            "a predicate-gated single-token path must not be hidden by a multi-token path"
13627        );
13628    }
13629
13630    #[test]
13631    fn left_recursive_loop_defers_predicate_guarded_operator() {
13632        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
13633        let mut parser = mini_parser_with_hooks(
13634            vec![
13635                TestToken::new(1).with_text("operator"),
13636                TestToken::eof("parser-test", 1, 1, 1),
13637            ],
13638            RejectingPredicateHooks::default(),
13639        );
13640        parser.rule_context_stack = vec![RuleContextFrame {
13641            rule_index: 0,
13642            invoking_state: -1,
13643        }];
13644
13645        assert_eq!(
13646            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13647            None,
13648            "a false predicate must be evaluated before entering the operator alternative"
13649        );
13650        assert_eq!(
13651            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
13652            None,
13653            "cached predicate-dependent lookahead must keep deferring"
13654        );
13655    }
13656
13657    #[test]
13658    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
13659        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
13660        let mut parser = parser_inside_left_recursive_callee(1);
13661
13662        assert_eq!(
13663            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13664            None
13665        );
13666        assert_eq!(
13667            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
13668            None,
13669            "the cached overlap must preserve the nullable child return path"
13670        );
13671    }
13672
13673    #[test]
13674    fn left_recursive_loop_defers_through_nullable_parent_return() {
13675        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
13676        let mut parser = mini_parser(vec![
13677            TestToken::new(1).with_text("lookahead"),
13678            TestToken::eof("parser-test", 1, 1, 1),
13679        ]);
13680        parser.rule_context_stack = vec![
13681            RuleContextFrame {
13682                rule_index: 0,
13683                invoking_state: -1,
13684            },
13685            RuleContextFrame {
13686                rule_index: 1,
13687                invoking_state: 1,
13688            },
13689            RuleContextFrame {
13690                rule_index: 2,
13691                invoking_state: 5,
13692            },
13693        ];
13694
13695        assert_eq!(
13696            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13697            None,
13698            "a nullable caller must unwind to its parent's consuming follow path"
13699        );
13700        assert_eq!(
13701            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
13702            None,
13703            "the caller-overlap cache must not retain a false negative"
13704        );
13705    }
13706
13707    #[test]
13708    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
13709        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
13710        let mut parser = mini_parser(vec![
13711            TestToken::new(1).with_text("lookahead"),
13712            TestToken::eof("parser-test", 1, 1, 1),
13713        ]);
13714        parser.rule_context_stack = vec![
13715            RuleContextFrame {
13716                rule_index: 0,
13717                invoking_state: -1,
13718            },
13719            RuleContextFrame {
13720                rule_index: 1,
13721                invoking_state: 1,
13722            },
13723            RuleContextFrame {
13724                rule_index: 1,
13725                invoking_state: 8,
13726            },
13727        ];
13728
13729        assert_eq!(
13730            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13731            None,
13732            "a recursive operand return must preserve its parent caller context"
13733        );
13734        assert_eq!(
13735            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
13736            None,
13737            "the caller-overlap cache must preserve the loop-boundary return"
13738        );
13739    }
13740
13741    fn token_then_eof_atn() -> Atn {
13742        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13743            4, 1, 2, // 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 token 1
13756            1, 2, 5, -1, 0, 0, // match EOF
13757            0, // decisions
13758        ]))
13759        .deserialize_parser()
13760        .expect("artificial parser ATN should deserialize")
13761    }
13762
13763    fn epsilon_cycle_atn() -> Atn {
13764        let mut atn = ParserAtnBuilder::new(1);
13765        for (state_number, kind) in [
13766            (0, AtnStateKind::RuleStart),
13767            (1, AtnStateKind::Basic),
13768            (2, AtnStateKind::RuleStop),
13769        ] {
13770            assert_eq!(
13771                atn.add_state(kind, Some(0)).expect("state").index(),
13772                state_number
13773            );
13774        }
13775        atn.set_rule_to_start_state(vec![0])
13776            .expect("rule start states");
13777        atn.set_rule_to_stop_state(vec![2])
13778            .expect("rule stop states");
13779        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13780            .expect("transition");
13781        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
13782            .expect("self-cycle transition");
13783        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13784            .expect("exit transition");
13785        finish_atn(atn)
13786    }
13787
13788    fn eof_then_action_atn() -> Atn {
13789        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13790            4, 1, 1, // version, parser, max token type
13791            3, // states
13792            2, 0, // rule start
13793            1, 0, // basic
13794            7, 0, // rule stop
13795            0, // non-greedy states
13796            0, // precedence states
13797            1, // rules
13798            0, // rule 0 start
13799            0, // modes
13800            0, // sets
13801            2, // transitions
13802            0, 1, 5, -1, 0, 0, // match EOF
13803            1, 2, 6, 0, 0, 0, // parser action
13804            0, // decisions
13805        ]))
13806        .deserialize_parser()
13807        .expect("artificial parser ATN should deserialize")
13808    }
13809
13810    fn noop_action_then_token_then_eof_atn() -> Atn {
13811        AtnDeserializer::new(&SerializedAtn::from_i32(&[
13812            4, 1, 2, // version, parser, max token type
13813            4, // states
13814            2, 0, // rule start
13815            1, 0, // basic
13816            1, 0, // basic
13817            7, 0, // rule stop
13818            0, // non-greedy states
13819            0, // precedence states
13820            1, // rules
13821            0, // rule 0 start
13822            0, // modes
13823            0, // sets
13824            3, // transitions
13825            0, 1, 6, 0, -1, 0, // no-op parser action
13826            1, 2, 5, 1, 0, 0, // match token 1
13827            2, 3, 5, -1, 0, 0, // match EOF
13828            0, // decisions
13829        ]))
13830        .deserialize_parser()
13831        .expect("artificial no-op action ATN should deserialize")
13832    }
13833
13834    fn two_alt_decision_atn() -> Atn {
13835        let mut atn = ParserAtnBuilder::new(2);
13836        assert_eq!(
13837            atn.add_state(AtnStateKind::RuleStart, Some(0))
13838                .expect("state")
13839                .index(),
13840            0
13841        );
13842        assert_eq!(
13843            atn.add_state(AtnStateKind::BlockStart, Some(0))
13844                .expect("state")
13845                .index(),
13846            1
13847        );
13848        assert_eq!(
13849            atn.add_state(AtnStateKind::Basic, Some(0))
13850                .expect("state")
13851                .index(),
13852            2
13853        );
13854        assert_eq!(
13855            atn.add_state(AtnStateKind::Basic, Some(0))
13856                .expect("state")
13857                .index(),
13858            3
13859        );
13860        assert_eq!(
13861            atn.add_state(AtnStateKind::BlockEnd, Some(0))
13862                .expect("state")
13863                .index(),
13864            4
13865        );
13866        assert_eq!(
13867            atn.add_state(AtnStateKind::RuleStop, Some(0))
13868                .expect("state")
13869                .index(),
13870            5
13871        );
13872        atn.set_rule_to_start_state(vec![0])
13873            .expect("rule start states");
13874        atn.set_rule_to_stop_state(vec![5])
13875            .expect("rule stop states");
13876        atn.add_decision_state(1).expect("decision state");
13877        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13878            .expect("transition");
13879        atn.add_transition(
13880            1,
13881            ParserTransitionSpec::Atom {
13882                target: 2,
13883                label: 1,
13884            },
13885        )
13886        .expect("transition");
13887        atn.add_transition(
13888            1,
13889            ParserTransitionSpec::Atom {
13890                target: 3,
13891                label: 2,
13892            },
13893        )
13894        .expect("transition");
13895        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
13896            .expect("transition");
13897        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13898            .expect("transition");
13899        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13900            .expect("transition");
13901        finish_atn(atn)
13902    }
13903
13904    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
13905    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
13906    fn optional_then_b_eof_atn() -> Atn {
13907        let mut atn = ParserAtnBuilder::new(3);
13908        assert_eq!(
13909            atn.add_state(AtnStateKind::RuleStart, Some(0))
13910                .expect("state")
13911                .index(),
13912            0
13913        );
13914        assert_eq!(
13915            atn.add_state(AtnStateKind::BlockStart, Some(0))
13916                .expect("state")
13917                .index(),
13918            1
13919        );
13920        assert_eq!(
13921            atn.add_state(AtnStateKind::Basic, Some(0))
13922                .expect("state")
13923                .index(),
13924            2
13925        );
13926        assert_eq!(
13927            atn.add_state(AtnStateKind::Basic, Some(0))
13928                .expect("state")
13929                .index(),
13930            3
13931        );
13932        assert_eq!(
13933            atn.add_state(AtnStateKind::Basic, Some(0))
13934                .expect("state")
13935                .index(),
13936            4
13937        );
13938        assert_eq!(
13939            atn.add_state(AtnStateKind::RuleStop, Some(0))
13940                .expect("state")
13941                .index(),
13942            5
13943        );
13944        atn.set_rule_to_start_state(vec![0])
13945            .expect("rule start states");
13946        atn.set_rule_to_stop_state(vec![5])
13947            .expect("rule stop states");
13948        atn.add_decision_state(1).expect("decision state");
13949        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13950            .expect("transition");
13951        // Optional block: match A then fall through, or skip straight to state 3.
13952        atn.add_transition(
13953            1,
13954            ParserTransitionSpec::Atom {
13955                target: 3,
13956                label: 1,
13957            },
13958        )
13959        .expect("transition");
13960        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
13961            .expect("transition");
13962        // Match B, then EOF.
13963        atn.add_transition(
13964            3,
13965            ParserTransitionSpec::Atom {
13966                target: 4,
13967                label: 2,
13968            },
13969        )
13970        .expect("transition");
13971        atn.add_transition(
13972            4,
13973            ParserTransitionSpec::Atom {
13974                target: 5,
13975                label: TOKEN_EOF,
13976            },
13977        )
13978        .expect("transition");
13979        finish_atn(atn)
13980    }
13981
13982    #[test]
13983    fn sync_decision_deletes_only_a_single_token() {
13984        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
13985        // expected. `(A)? B EOF` at the optional-block decision:
13986        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
13987        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
13988        //               without consuming and records the expected set for the
13989        //               subsequent mismatch (the parser must not over-consume both
13990        //               `C`s and accept the input).
13991        let atn = optional_then_b_eof_atn();
13992
13993        let mut single = mini_parser(vec![
13994            TestToken::new(3).with_text("c"),
13995            TestToken::new(2).with_text("b"),
13996            TestToken::eof("parser-test", 1, 2, 2),
13997        ]);
13998        single.rule_context_stack = vec![RuleContextFrame {
13999            rule_index: 0,
14000            invoking_state: 0,
14001        }];
14002        let children = single
14003            .sync_decision(&atn, 1, true, false)
14004            .expect("single extraneous token recovers");
14005        assert_eq!(children.len(), 1);
14006        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
14007        assert_eq!(single.number_of_syntax_errors(), 1);
14008        // Exactly one token consumed (the cursor now sits on `b`).
14009        assert_eq!(single.la(1), 2);
14010
14011        let mut double = mini_parser(vec![
14012            TestToken::new(3).with_text("c"),
14013            TestToken::new(3).with_text("c"),
14014            TestToken::new(2).with_text("b"),
14015            TestToken::eof("parser-test", 1, 3, 3),
14016        ]);
14017        double.rule_context_stack = vec![RuleContextFrame {
14018            rule_index: 0,
14019            invoking_state: 0,
14020        }];
14021        let result = double.sync_decision(&atn, 1, true, false);
14022        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
14023        // consume either `c`. It reports the mismatch at the first `c` (so the parser
14024        // does not over-consume both and accept the input). Nothing is consumed, so
14025        // the cursor still sits on the first `c` for rule-level recovery.
14026        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
14027        match error {
14028            AntlrError::ParserError { message, .. } => {
14029                assert!(message.starts_with("mismatched input"), "got: {message}");
14030            }
14031            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
14032        }
14033        assert_eq!(double.la(1), 3);
14034    }
14035
14036    /// The real serialized ATN that `antlr4-rust-gen` emits for
14037    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
14038    /// the loop is `EOF`. The loop decision is state 5.
14039    fn star_loop_then_eof_atn() -> Atn {
14040        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14041            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,
14042            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,
14043            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,
14044            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
14045        ]))
14046        .deserialize_parser()
14047        .expect("star-loop-then-EOF ATN should deserialize")
14048    }
14049
14050    /// ATN for `s : a+ Y ; a : X ;`.
14051    ///
14052    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
14053    /// `+` loop must not treat that zero-width child as a successful iteration
14054    /// and then re-enter the loop at the same token index.
14055    fn plus_loop_with_recovering_body_atn() -> Atn {
14056        let mut atn = ParserAtnBuilder::new(2);
14057        assert_eq!(
14058            atn.add_state(AtnStateKind::RuleStart, Some(0))
14059                .expect("state")
14060                .index(),
14061            0
14062        );
14063        assert_eq!(
14064            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
14065                .expect("state")
14066                .index(),
14067            1
14068        );
14069        assert_eq!(
14070            atn.add_state(AtnStateKind::Basic, Some(0))
14071                .expect("state")
14072                .index(),
14073            2
14074        );
14075        assert_eq!(
14076            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14077                .expect("state")
14078                .index(),
14079            3
14080        );
14081        assert_eq!(
14082            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
14083                .expect("state")
14084                .index(),
14085            4
14086        );
14087        assert_eq!(
14088            atn.add_state(AtnStateKind::LoopEnd, Some(0))
14089                .expect("state")
14090                .index(),
14091            5
14092        );
14093        assert_eq!(
14094            atn.add_state(AtnStateKind::RuleStop, Some(0))
14095                .expect("state")
14096                .index(),
14097            6
14098        );
14099        assert_eq!(
14100            atn.add_state(AtnStateKind::RuleStart, Some(1))
14101                .expect("state")
14102                .index(),
14103            7
14104        );
14105        assert_eq!(
14106            atn.add_state(AtnStateKind::Basic, Some(1))
14107                .expect("state")
14108                .index(),
14109            8
14110        );
14111        assert_eq!(
14112            atn.add_state(AtnStateKind::RuleStop, Some(1))
14113                .expect("state")
14114                .index(),
14115            9
14116        );
14117        atn.set_rule_to_start_state(vec![0, 7])
14118            .expect("rule start states");
14119        atn.set_rule_to_stop_state(vec![6, 9])
14120            .expect("rule stop states");
14121        atn.set_end_state(1, 3).expect("block end state");
14122        atn.set_loop_back_state(5, 4).expect("loop back state");
14123        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14124            .expect("transition");
14125        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14126            .expect("transition");
14127        atn.add_transition(
14128            2,
14129            ParserTransitionSpec::Rule {
14130                target: 7,
14131                rule_index: 1,
14132                follow_state: 3,
14133                precedence: 0,
14134            },
14135        )
14136        .expect("transition");
14137        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14138            .expect("transition");
14139        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
14140            .expect("transition");
14141        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14142            .expect("transition");
14143        atn.add_transition(
14144            5,
14145            ParserTransitionSpec::Atom {
14146                target: 6,
14147                label: 2,
14148            },
14149        )
14150        .expect("transition");
14151        atn.add_transition(
14152            7,
14153            ParserTransitionSpec::Atom {
14154                target: 8,
14155                label: 1,
14156            },
14157        )
14158        .expect("transition");
14159        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14160            .expect("transition");
14161        finish_atn(atn)
14162    }
14163
14164    #[test]
14165    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
14166        let atn = plus_loop_with_recovering_body_atn();
14167        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14168
14169        let error = parser
14170            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
14171            .expect_err("EOF recovery should report a bounded mismatch");
14172
14173        let AntlrError::ParserError { message, .. } = error else {
14174            panic!("expected ParserError, got {error:?}");
14175        };
14176        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
14177        assert_eq!(parser.number_of_syntax_errors(), 1);
14178        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
14179    }
14180
14181    #[test]
14182    fn sync_decision_deletes_token_before_eof_at_loop_back() {
14183        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
14184        // At the loop ENTRY (loop_back = false) a single unexpected token before
14185        // EOF is deleted as an error node (then the generated EOF match consumes
14186        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
14187        // EOF must be a valid scan-stop for this to fire.
14188        let atn = star_loop_then_eof_atn();
14189        let mut parser = mini_parser(vec![
14190            TestToken::new(2).with_text("c"),
14191            TestToken::eof("parser-test", 1, 1, 1),
14192        ]);
14193        parser.rule_context_stack = vec![RuleContextFrame {
14194            rule_index: 0,
14195            invoking_state: 0,
14196        }];
14197        let children = parser
14198            .sync_decision(&atn, 5, true, false)
14199            .expect("single token before EOF recovers");
14200        assert_eq!(children.len(), 1);
14201        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
14202        assert_eq!(parser.number_of_syntax_errors(), 1);
14203        assert_eq!(
14204            parser.la(1),
14205            TOKEN_EOF,
14206            "EOF is left for the rule's EOF match"
14207        );
14208    }
14209
14210    #[test]
14211    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
14212        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
14213        // single-token deletion, which fails because LA(2) = `c` is not expected —
14214        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
14215        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
14216        let atn = star_loop_then_eof_atn();
14217        let mut parser = mini_parser(vec![
14218            TestToken::new(2).with_text("c"),
14219            TestToken::new(2).with_text("c"),
14220            TestToken::eof("parser-test", 1, 2, 2),
14221        ]);
14222        parser.rule_context_stack = vec![RuleContextFrame {
14223            rule_index: 0,
14224            invoking_state: 0,
14225        }];
14226        let error = parser
14227            .sync_decision(&atn, 5, true, false)
14228            .expect_err("two tokens at the loop entry must not be deleted");
14229        match error {
14230            AntlrError::ParserError { message, .. } => {
14231                assert!(message.starts_with("mismatched input"), "got: {message}");
14232            }
14233            other => panic!("expected mismatched-input ParserError, got {other:?}"),
14234        }
14235        assert_eq!(
14236            parser.la(1),
14237            2,
14238            "nothing consumed; cursor still on first `c`"
14239        );
14240    }
14241
14242    #[test]
14243    fn sync_decision_consumes_until_eof_at_loop_back() {
14244        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
14245        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
14246        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
14247        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
14248        // post-`a` state directly: `c c <EOF>` with loop_back = true.
14249        let atn = star_loop_then_eof_atn();
14250        let mut parser = mini_parser(vec![
14251            TestToken::new(2).with_text("c"),
14252            TestToken::new(2).with_text("c"),
14253            TestToken::eof("parser-test", 1, 2, 2),
14254        ]);
14255        parser.rule_context_stack = vec![RuleContextFrame {
14256            rule_index: 0,
14257            invoking_state: 0,
14258        }];
14259        let children = parser
14260            .sync_decision(&atn, 5, false, true)
14261            .expect("loop-back multi-token deletion recovers onto EOF");
14262        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
14263        assert!(
14264            children
14265                .iter()
14266                .all(|child| parser.node(*child).kind() == NodeKind::Error)
14267        );
14268        assert_eq!(parser.number_of_syntax_errors(), 1);
14269        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
14270    }
14271
14272    fn predicate_after_token_atn() -> Atn {
14273        let mut atn = ParserAtnBuilder::new(2);
14274        assert_eq!(
14275            atn.add_state(AtnStateKind::RuleStart, Some(0))
14276                .expect("state")
14277                .index(),
14278            0
14279        );
14280        assert_eq!(
14281            atn.add_state(AtnStateKind::Basic, Some(0))
14282                .expect("state")
14283                .index(),
14284            1
14285        );
14286        assert_eq!(
14287            atn.add_state(AtnStateKind::Basic, Some(0))
14288                .expect("state")
14289                .index(),
14290            2
14291        );
14292        assert_eq!(
14293            atn.add_state(AtnStateKind::Basic, Some(0))
14294                .expect("state")
14295                .index(),
14296            3
14297        );
14298        assert_eq!(
14299            atn.add_state(AtnStateKind::RuleStop, Some(0))
14300                .expect("state")
14301                .index(),
14302            4
14303        );
14304        atn.set_rule_to_start_state(vec![0])
14305            .expect("rule start states");
14306        atn.set_rule_to_stop_state(vec![4])
14307            .expect("rule stop states");
14308        atn.add_transition(
14309            0,
14310            ParserTransitionSpec::Atom {
14311                target: 1,
14312                label: 1,
14313            },
14314        )
14315        .expect("transition");
14316        atn.add_transition(
14317            1,
14318            ParserTransitionSpec::Predicate {
14319                target: 2,
14320                rule_index: 0,
14321                pred_index: 0,
14322                context_dependent: false,
14323            },
14324        )
14325        .expect("transition");
14326        atn.add_transition(
14327            2,
14328            ParserTransitionSpec::Atom {
14329                target: 3,
14330                label: 2,
14331            },
14332        )
14333        .expect("transition");
14334        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14335            .expect("transition");
14336        finish_atn(atn)
14337    }
14338
14339    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
14340        let mut atn = ParserAtnBuilder::new(1);
14341        for (state_number, kind) in [
14342            (0, AtnStateKind::RuleStart),
14343            (1, AtnStateKind::BlockStart),
14344            (2, AtnStateKind::Basic),
14345            (3, AtnStateKind::Basic),
14346            (4, AtnStateKind::Basic),
14347            (5, AtnStateKind::Basic),
14348            (6, AtnStateKind::BlockEnd),
14349            (7, AtnStateKind::RuleStop),
14350        ] {
14351            assert_eq!(
14352                atn.add_state(kind, Some(0)).expect("state").index(),
14353                state_number
14354            );
14355        }
14356        atn.set_rule_to_start_state(vec![0])
14357            .expect("rule start states");
14358        atn.set_rule_to_stop_state(vec![7])
14359            .expect("rule stop states");
14360        atn.add_decision_state(1).expect("decision state");
14361        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14362            .expect("transition");
14363        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14364            .expect("transition");
14365        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14366            .expect("transition");
14367        atn.add_transition(
14368            2,
14369            ParserTransitionSpec::Predicate {
14370                target: 4,
14371                rule_index: 0,
14372                pred_index: pred_indexes[0],
14373                context_dependent: false,
14374            },
14375        )
14376        .expect("transition");
14377        atn.add_transition(
14378            3,
14379            ParserTransitionSpec::Predicate {
14380                target: 5,
14381                rule_index: 0,
14382                pred_index: pred_indexes[1],
14383                context_dependent: false,
14384            },
14385        )
14386        .expect("transition");
14387        atn.add_transition(
14388            4,
14389            ParserTransitionSpec::Atom {
14390                target: 6,
14391                label: 1,
14392            },
14393        )
14394        .expect("transition");
14395        atn.add_transition(
14396            5,
14397            ParserTransitionSpec::Atom {
14398                target: 6,
14399                label: 1,
14400            },
14401        )
14402        .expect("transition");
14403        atn.add_transition(
14404            6,
14405            ParserTransitionSpec::Atom {
14406                target: 7,
14407                label: TOKEN_EOF,
14408            },
14409        )
14410        .expect("transition");
14411        finish_atn(atn)
14412    }
14413
14414    fn nested_nullable_context_atn() -> Atn {
14415        let mut atn = ParserAtnBuilder::new(1);
14416        for state_number in 0..=20 {
14417            let kind = match state_number {
14418                0 | 10 | 16 => AtnStateKind::RuleStart,
14419                9 | 15 | 20 => AtnStateKind::RuleStop,
14420                _ => AtnStateKind::Basic,
14421            };
14422            let rule_index = match state_number {
14423                0..=9 => 0,
14424                10..=15 => 1,
14425                _ => 2,
14426            };
14427            assert_eq!(
14428                atn.add_state(kind, Some(rule_index))
14429                    .expect("state")
14430                    .index(),
14431                state_number
14432            );
14433        }
14434        atn.set_rule_to_start_state(vec![0, 10, 16])
14435            .expect("rule start states");
14436        atn.set_rule_to_stop_state(vec![9, 15, 20])
14437            .expect("rule stop states");
14438        atn.add_transition(
14439            1,
14440            ParserTransitionSpec::Rule {
14441                target: 10,
14442                rule_index: 1,
14443                follow_state: 8,
14444                precedence: 0,
14445            },
14446        )
14447        .expect("transition");
14448        atn.add_transition(
14449            8,
14450            ParserTransitionSpec::Atom {
14451                target: 9,
14452                label: 1,
14453            },
14454        )
14455        .expect("transition");
14456        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14457            .expect("transition");
14458        atn.add_transition(
14459            2,
14460            ParserTransitionSpec::Rule {
14461                target: 16,
14462                rule_index: 2,
14463                follow_state: 14,
14464                precedence: 0,
14465            },
14466        )
14467        .expect("transition");
14468        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
14469            .expect("transition");
14470        finish_atn(atn)
14471    }
14472
14473    fn generated_match_recovery_atn() -> Atn {
14474        let mut atn = ParserAtnBuilder::new(2);
14475        assert_eq!(
14476            atn.add_state(AtnStateKind::RuleStart, Some(0))
14477                .expect("state")
14478                .index(),
14479            0
14480        );
14481        assert_eq!(
14482            atn.add_state(AtnStateKind::Basic, Some(0))
14483                .expect("state")
14484                .index(),
14485            1
14486        );
14487        assert_eq!(
14488            atn.add_state(AtnStateKind::Basic, Some(0))
14489                .expect("state")
14490                .index(),
14491            2
14492        );
14493        assert_eq!(
14494            atn.add_state(AtnStateKind::RuleStop, Some(0))
14495                .expect("state")
14496                .index(),
14497            3
14498        );
14499        assert_eq!(
14500            atn.add_state(AtnStateKind::RuleStart, Some(1))
14501                .expect("state")
14502                .index(),
14503            4
14504        );
14505        assert_eq!(
14506            atn.add_state(AtnStateKind::RuleStop, Some(1))
14507                .expect("state")
14508                .index(),
14509            5
14510        );
14511        atn.set_rule_to_start_state(vec![0, 4])
14512            .expect("rule start states");
14513        atn.set_rule_to_stop_state(vec![3, 5])
14514            .expect("rule stop states");
14515        atn.add_transition(
14516            1,
14517            ParserTransitionSpec::Rule {
14518                target: 4,
14519                rule_index: 1,
14520                follow_state: 2,
14521                precedence: 0,
14522            },
14523        )
14524        .expect("transition");
14525        atn.add_transition(
14526            2,
14527            ParserTransitionSpec::Atom {
14528                target: 3,
14529                label: TOKEN_EOF,
14530            },
14531        )
14532        .expect("transition");
14533        finish_atn(atn)
14534    }
14535
14536    fn complement_set_atn() -> Atn {
14537        let mut atn = ParserAtnBuilder::new(1);
14538        assert_eq!(
14539            atn.add_state(AtnStateKind::RuleStart, Some(0))
14540                .expect("state")
14541                .index(),
14542            0
14543        );
14544        assert_eq!(
14545            atn.add_state(AtnStateKind::RuleStop, Some(0))
14546                .expect("state")
14547                .index(),
14548            1
14549        );
14550        atn.set_rule_to_start_state(vec![0])
14551            .expect("rule start states");
14552        atn.set_rule_to_stop_state(vec![1])
14553            .expect("rule stop states");
14554        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
14555        atn.add_transition(
14556            0,
14557            ParserTransitionSpec::NotSet {
14558                target: 1,
14559                set: excluded,
14560            },
14561        )
14562        .expect("transition");
14563        finish_atn(atn)
14564    }
14565
14566    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
14567    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
14568    fn wildcard_then_eof_atn() -> Atn {
14569        let mut atn = ParserAtnBuilder::new(1);
14570        assert_eq!(
14571            atn.add_state(AtnStateKind::RuleStart, Some(0))
14572                .expect("state")
14573                .index(),
14574            0
14575        );
14576        assert_eq!(
14577            atn.add_state(AtnStateKind::RuleStop, Some(0))
14578                .expect("state")
14579                .index(),
14580            1
14581        );
14582        assert_eq!(
14583            atn.add_state(AtnStateKind::Basic, Some(0))
14584                .expect("state")
14585                .index(),
14586            2
14587        );
14588        atn.set_rule_to_start_state(vec![0])
14589            .expect("rule start states");
14590        atn.set_rule_to_stop_state(vec![1])
14591            .expect("rule stop states");
14592        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
14593            .expect("transition");
14594        atn.add_transition(
14595            2,
14596            ParserTransitionSpec::Atom {
14597                target: 1,
14598                label: TOKEN_EOF,
14599            },
14600        )
14601        .expect("transition");
14602        finish_atn(atn)
14603    }
14604
14605    #[test]
14606    fn parser_matches_token_and_reports_mismatch() {
14607        let source = Source {
14608            tokens: vec![
14609                TestToken::new(1).with_text("x"),
14610                TestToken::eof("parser-test", 1, 1, 1),
14611            ],
14612            index: 0,
14613        };
14614        let data = RecognizerData::new(
14615            "Mini.g4",
14616            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14617        );
14618        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
14619        let matched = parser.match_token(1).expect("token 1 should match");
14620        assert_eq!(parser.node(matched).text(), "x");
14621        assert!(parser.match_token(1).is_err());
14622    }
14623
14624    #[test]
14625    fn parser_matches_token_sets() {
14626        let mut parser = mini_parser(vec![
14627            TestToken::new(1).with_text("x"),
14628            TestToken::eof("parser-test", 1, 1, 1),
14629        ]);
14630
14631        let matched = parser
14632            .match_set(&[(1, 1), (3, 4)])
14633            .expect("token set should match");
14634        assert_eq!(parser.node(matched).text(), "x");
14635        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
14636    }
14637
14638    #[test]
14639    fn generated_rule_api_tracks_state_and_precedence() {
14640        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14641
14642        let context = parser.enter_rule(7, 2);
14643        assert_eq!(context.rule_index(), 2);
14644        assert_eq!(parser.state(), 7);
14645        assert_eq!(
14646            parser.rule_context_stack,
14647            vec![RuleContextFrame {
14648                rule_index: 2,
14649                invoking_state: 7
14650            }]
14651        );
14652
14653        let recursive = parser.enter_recursion_rule(11, 3, 4);
14654        assert_eq!(recursive.rule_index(), 3);
14655        assert!(parser.precpred(4));
14656        assert!(parser.precpred(5));
14657        assert!(!parser.precpred(3));
14658
14659        let next = parser.push_new_recursion_context(13, 3);
14660        assert_eq!(next.invoking_state(), 13);
14661        parser.unroll_recursion_context();
14662        assert_eq!(parser.precedence_stack, vec![0]);
14663        assert_eq!(
14664            parser.rule_context_stack,
14665            vec![RuleContextFrame {
14666                rule_index: 2,
14667                invoking_state: 7
14668            }]
14669        );
14670
14671        parser.exit_rule();
14672        assert!(parser.rule_context_stack.is_empty());
14673    }
14674
14675    #[test]
14676    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
14677        let mut parser = mini_parser(vec![
14678            TestToken::new(1).with_text("x"),
14679            TestToken::eof("parser-test", 1, 1, 1),
14680        ]);
14681        let matched = parser.match_token(1).expect("token should match");
14682        assert_eq!(parser.node(matched).text(), "x");
14683        parser.record_generated_syntax_error();
14684        parser.set_int_member(7, 11);
14685        parser.set_build_parse_trees(false);
14686        parser.set_report_diagnostic_errors(true);
14687        parser.set_prediction_mode(PredictionMode::Sll);
14688        parser.set_bail_on_error(true);
14689        let _context = parser.enter_recursion_rule(9, 0, 4);
14690        parser.pending_invoking_states.push(5);
14691        parser.unknown_predicate_hits.push((0, 1));
14692        parser.unhandled_action_hits.push((0, 2));
14693
14694        parser.reset();
14695
14696        assert_eq!(parser.input.index(), 0);
14697        assert_eq!(parser.la(1), 1);
14698        assert_eq!(parser.state(), -1);
14699        assert_eq!(parser.number_of_syntax_errors(), 0);
14700        assert_eq!(parser.parse_tree_storage().node_count(), 0);
14701        assert!(parser.rule_context_stack.is_empty());
14702        assert!(parser.pending_invoking_states.is_empty());
14703        assert_eq!(parser.precedence_stack, [0]);
14704        assert!(parser.unknown_predicate_hits.is_empty());
14705        assert!(parser.unhandled_action_hits.is_empty());
14706        assert_eq!(parser.int_member(7), Some(11));
14707        assert!(!parser.build_parse_trees());
14708        assert!(parser.report_diagnostic_errors());
14709        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
14710        assert!(parser.bail_on_error());
14711    }
14712
14713    #[test]
14714    fn set_token_stream_replaces_input_and_resets_parser() {
14715        let mut parser = mini_parser(vec![
14716            TestToken::new(1).with_text("old"),
14717            TestToken::eof("parser-test", 1, 1, 1),
14718        ]);
14719        parser.consume();
14720        parser.record_generated_syntax_error();
14721        let replacement = CommonTokenStream::new(Source {
14722            tokens: vec![
14723                TestToken::new(2).with_text("new"),
14724                TestToken::eof("parser-test", 1, 1, 1),
14725            ],
14726            index: 0,
14727        });
14728
14729        parser.set_token_stream(replacement);
14730
14731        assert_eq!(parser.input.index(), 0);
14732        assert_eq!(parser.la(1), 2);
14733        assert_eq!(parser.input.text_all(), "new");
14734        assert_eq!(parser.number_of_syntax_errors(), 0);
14735    }
14736
14737    #[test]
14738    fn active_invocation_states_exclude_the_root_frame() {
14739        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14740
14741        let _root = parser.enter_rule(0, 0);
14742        assert!(parser.active_invocation_states().is_empty());
14743
14744        let marker = parser.push_invoking_state(6);
14745        let _child = parser.enter_rule(2, 1);
14746        parser.discard_invoking_state(marker);
14747        assert_eq!(parser.active_invocation_states(), [6]);
14748
14749        let marker = parser.push_invoking_state(13);
14750        let _grandchild = parser.enter_rule(4, 2);
14751        parser.discard_invoking_state(marker);
14752        assert_eq!(parser.active_invocation_states(), [13, 6]);
14753
14754        parser.exit_rule();
14755        parser.exit_rule();
14756        parser.exit_rule();
14757    }
14758
14759    #[test]
14760    fn parser_predicates_support_token_adjacency() {
14761        let mut parser = mini_parser(vec![
14762            TestToken::new(1).with_text("=").with_span(0, 0),
14763            TestToken::new(1).with_text(">").with_span(1, 1),
14764            TestToken::eof("parser-test", 2, 1, 2),
14765        ]);
14766        parser.consume();
14767        parser.consume();
14768
14769        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
14770
14771        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14772
14773        let mut parser = mini_parser(vec![
14774            TestToken::new(1).with_text("=").with_span(0, 0),
14775            TestToken::new(1)
14776                .with_text(" ")
14777                .with_channel(HIDDEN_CHANNEL)
14778                .with_span(1, 1),
14779            TestToken::new(1).with_text(">").with_span(2, 2),
14780            TestToken::eof("parser-test", 3, 1, 3),
14781        ]);
14782        parser.consume();
14783        parser.consume();
14784
14785        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
14786    }
14787
14788    #[test]
14789    fn parser_predicates_support_context_child_text_checks() {
14790        let mut parser = mini_parser(vec![
14791            TestToken::new(1).with_text("var"),
14792            TestToken::eof("parser-test", 1, 1, 1),
14793        ]);
14794        let mut context = ParserRuleContext::new(1, 0);
14795        let mut child_context = ParserRuleContext::new(2, 0);
14796        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
14797        parser.tree.add_child(&mut child_context, terminal);
14798        let child = parser.rule_node(child_context);
14799        parser.tree.add_child(&mut context, child);
14800        let predicates = [(
14801            1,
14802            0,
14803            ParserPredicate::ContextChildRuleTextNotEquals {
14804                rule_index: 2,
14805                text: "var",
14806            },
14807        )];
14808
14809        assert!(
14810            !parser.parser_semantic_predicate_matches_with_context_and_local(
14811                &predicates,
14812                1,
14813                0,
14814                &context,
14815                0,
14816            )
14817        );
14818    }
14819
14820    #[test]
14821    fn context_expected_symbols_walks_nullable_parent_contexts() {
14822        let atn = nested_nullable_context_atn();
14823        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14824        parser.rule_context_stack = vec![
14825            RuleContextFrame {
14826                rule_index: 0,
14827                invoking_state: 0,
14828            },
14829            RuleContextFrame {
14830                rule_index: 1,
14831                invoking_state: 1,
14832            },
14833            RuleContextFrame {
14834                rule_index: 2,
14835                invoking_state: 2,
14836            },
14837        ];
14838
14839        let expected = parser.context_expected_symbols(&atn);
14840
14841        assert!(expected.contains(&1));
14842        assert!(expected.contains(&TOKEN_EOF));
14843    }
14844
14845    #[test]
14846    fn prediction_context_return_states_track_rule_stack_changes() {
14847        let atn = nested_nullable_context_atn();
14848        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
14849        parser.rule_context_stack = vec![
14850            RuleContextFrame {
14851                rule_index: 0,
14852                invoking_state: 0,
14853            },
14854            RuleContextFrame {
14855                rule_index: 1,
14856                invoking_state: 1,
14857            },
14858            RuleContextFrame {
14859                rule_index: 2,
14860                invoking_state: 2,
14861            },
14862        ];
14863
14864        let initial_version = parser.rule_context_version();
14865        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14866        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14867        assert_eq!(first, second);
14868        assert_eq!(parser.rule_context_version(), initial_version);
14869
14870        parser.exit_rule();
14871        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
14872        assert_ne!(first, after_pop);
14873        assert_ne!(parser.rule_context_version(), initial_version);
14874    }
14875
14876    #[test]
14877    fn generated_match_token_recovers_missing_token_from_context_follow() {
14878        let atn = generated_match_recovery_atn();
14879        let data = RecognizerData::new(
14880            "Mini.g4",
14881            Vocabulary::new(
14882                [None, Some("'X'"), Some("'Y'")],
14883                [None, Some("X"), Some("Y")],
14884                [None::<&str>, None, None],
14885            ),
14886        );
14887        let mut parser = BaseParser::new(
14888            CommonTokenStream::new(Source {
14889                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
14890                index: 0,
14891            }),
14892            data,
14893        );
14894        parser.rule_context_stack = vec![
14895            RuleContextFrame {
14896                rule_index: 0,
14897                invoking_state: 0,
14898            },
14899            RuleContextFrame {
14900                rule_index: 1,
14901                invoking_state: 1,
14902            },
14903        ];
14904        assert_eq!(parser.number_of_syntax_errors(), 0);
14905
14906        let node = parser
14907            .match_token_recovering(2, 5, &atn)
14908            .expect("generated match should insert missing token");
14909
14910        assert_eq!(node.children().len(), 1);
14911        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
14912        assert_eq!(
14913            node.clone()
14914                .into_child_iter()
14915                .map(|child| parser.node(child).text())
14916                .collect::<Vec<_>>(),
14917            ["<missing 'Y'>"]
14918        );
14919        // Single-token insertion synthesizes a missing token and consumes nothing,
14920        // so no EOF terminal is consumed even though lookahead is EOF.
14921        assert!(!node.consumed_eof());
14922        assert_eq!(parser.la(1), TOKEN_EOF);
14923        assert_eq!(parser.number_of_syntax_errors(), 1);
14924        assert_eq!(
14925            parser.generated_parser_diagnostics,
14926            [ParserDiagnostic {
14927                line: 1,
14928                column: 3,
14929                message: "missing 'Y' at '<EOF>'".to_owned(),
14930            }]
14931        );
14932    }
14933
14934    #[test]
14935    fn generated_match_token_counts_single_token_deletion_recovery() {
14936        let atn = generated_match_recovery_atn();
14937        let data = RecognizerData::new(
14938            "Mini.g4",
14939            Vocabulary::new(
14940                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
14941                [None, Some("X"), Some("Y"), Some("Z")],
14942                [None::<&str>, None, None, None],
14943            ),
14944        );
14945        let mut parser = BaseParser::new(
14946            CommonTokenStream::new(Source {
14947                tokens: vec![
14948                    TestToken::new(3).with_text("z"),
14949                    TestToken::new(2).with_text("y"),
14950                    TestToken::eof("parser-test", 3, 1, 3),
14951                ],
14952                index: 0,
14953            }),
14954            data,
14955        );
14956
14957        let node = parser
14958            .match_token_recovering(2, 5, &atn)
14959            .expect("generated match should delete the extraneous token");
14960
14961        assert_eq!(node.children().len(), 2);
14962        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
14963        assert_eq!(parser.node(node.children()[0]).text(), "z");
14964        assert_eq!(parser.node(node.children()[1]).text(), "y");
14965        assert_eq!(
14966            node.into_child_iter()
14967                .map(|child| parser.node(child).text())
14968                .collect::<Vec<_>>(),
14969            ["z", "y"]
14970        );
14971        assert_eq!(parser.number_of_syntax_errors(), 1);
14972    }
14973
14974    #[test]
14975    fn generated_match_token_iterates_single_success_without_a_children_vec() {
14976        let atn = generated_match_recovery_atn();
14977        let data = RecognizerData::new(
14978            "Mini.g4",
14979            Vocabulary::new(
14980                [None, Some("'X'"), Some("'Y'")],
14981                [None, Some("X"), Some("Y")],
14982                [None::<&str>, None, None],
14983            ),
14984        );
14985        let mut parser = BaseParser::new(
14986            CommonTokenStream::new(Source {
14987                tokens: vec![
14988                    TestToken::new(2).with_text("y"),
14989                    TestToken::eof("parser-test", 1, 1, 1),
14990                ],
14991                index: 0,
14992            }),
14993            data,
14994        );
14995
14996        let node = parser
14997            .match_token_recovering(2, 5, &atn)
14998            .expect("generated match should consume the expected token");
14999
15000        assert_eq!(
15001            node.into_child_iter()
15002                .map(|child| parser.node(child).text())
15003                .collect::<Vec<_>>(),
15004            ["y"]
15005        );
15006        assert_eq!(parser.number_of_syntax_errors(), 0);
15007    }
15008
15009    #[test]
15010    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
15011        let atn = generated_match_recovery_atn();
15012        let data = RecognizerData::new(
15013            "Mini.g4",
15014            Vocabulary::new(
15015                [None, Some("'X'"), Some("'Y'")],
15016                [None, Some("X"), Some("Y")],
15017                [None::<&str>, None, None],
15018            ),
15019        );
15020        let mut parser = BaseParser::new(
15021            CommonTokenStream::new(Source {
15022                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15023                index: 0,
15024            }),
15025            data,
15026        );
15027        parser.rule_context_stack = vec![
15028            RuleContextFrame {
15029                rule_index: 0,
15030                invoking_state: 0,
15031            },
15032            RuleContextFrame {
15033                rule_index: 1,
15034                invoking_state: 1,
15035            },
15036        ];
15037        let marker = parser.generated_diagnostics_checkpoint();
15038
15039        let _ = parser
15040            .match_token_recovering(2, 5, &atn)
15041            .expect("generated match should insert missing token");
15042        assert_eq!(parser.number_of_syntax_errors(), 1);
15043
15044        parser.restore_generated_diagnostics(marker);
15045
15046        assert_eq!(parser.number_of_syntax_errors(), 0);
15047        assert!(parser.generated_parser_diagnostics.is_empty());
15048    }
15049
15050    #[test]
15051    fn generated_prediction_diagnostics_use_adaptive_context() {
15052        let atn = two_alt_decision_atn();
15053        let data = RecognizerData::new(
15054            "Mini.g4",
15055            Vocabulary::new(
15056                [None, Some("'x'"), Some("'y'")],
15057                [None, Some("X"), Some("Y")],
15058                [None::<&str>, None, None],
15059            ),
15060        )
15061        .with_rule_names(["s"]);
15062        let mut parser = BaseParser::new(
15063            CommonTokenStream::new(Source {
15064                tokens: vec![
15065                    TestToken::new(1)
15066                        .with_text("x")
15067                        .with_position(1, 0)
15068                        .with_span(0, 0),
15069                    TestToken::new(2)
15070                        .with_text("y")
15071                        .with_position(1, 2)
15072                        .with_span(1, 1),
15073                    TestToken::eof("parser-test", 2, 1, 3),
15074                ],
15075                index: 0,
15076            }),
15077            data,
15078        );
15079        parser.set_report_diagnostic_errors(true);
15080
15081        parser.record_generated_prediction_diagnostic(
15082            &atn,
15083            1,
15084            &ParserAtnPrediction {
15085                alt: 1,
15086                requires_full_context: true,
15087                has_semantic_context: false,
15088                diagnostic: Some(ParserAtnPredictionDiagnostic {
15089                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
15090                    start_index: 0,
15091                    sll_stop_index: 1,
15092                    ll_stop_index: 0,
15093                    conflicting_alts: vec![1, 2],
15094                    exact: false,
15095                }),
15096            },
15097        );
15098        // Ambiguities from the default LL prediction mode are non-exact, so —
15099        // matching Java's exactOnly DiagnosticErrorListener — only the
15100        // attempting-full-context line is reported. Exact-ambiguity mode
15101        // reports the ambiguity itself.
15102        parser.record_generated_prediction_diagnostic(
15103            &atn,
15104            1,
15105            &ParserAtnPrediction {
15106                alt: 1,
15107                requires_full_context: true,
15108                has_semantic_context: false,
15109                diagnostic: Some(ParserAtnPredictionDiagnostic {
15110                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
15111                    start_index: 0,
15112                    sll_stop_index: 1,
15113                    ll_stop_index: 1,
15114                    conflicting_alts: vec![1, 2],
15115                    exact: false,
15116                }),
15117            },
15118        );
15119
15120        assert_eq!(
15121            parser.generated_parser_diagnostics,
15122            [
15123                ParserDiagnostic {
15124                    line: 1,
15125                    column: 2,
15126                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
15127                },
15128                ParserDiagnostic {
15129                    line: 1,
15130                    column: 0,
15131                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
15132                },
15133                ParserDiagnostic {
15134                    line: 1,
15135                    column: 2,
15136                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
15137                },
15138            ]
15139        );
15140    }
15141
15142    #[test]
15143    fn generated_match_not_set_recovers_empty_complement_at_eof() {
15144        let atn = complement_set_atn();
15145        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15146        parser.rule_context_stack = vec![RuleContextFrame {
15147            rule_index: 0,
15148            invoking_state: 0,
15149        }];
15150
15151        let node = parser
15152            .match_not_token_set_recovering(
15153                atn.token_set(0).expect("excluded token set"),
15154                1,
15155                1,
15156                1,
15157                &atn,
15158            )
15159            .expect("empty complement should recover at EOF");
15160
15161        assert_eq!(node.children().len(), 1);
15162        // Recovery synthesizes a missing token without consuming EOF, so the
15163        // enclosing rule must not record EOF as its stop token.
15164        assert!(!node.consumed_eof());
15165        assert_eq!(parser.la(1), TOKEN_EOF);
15166        assert_eq!(
15167            parser.generated_parser_diagnostics,
15168            [ParserDiagnostic {
15169                line: 1,
15170                column: 1,
15171                message: "missing {} at '<EOF>'".to_owned(),
15172            }]
15173        );
15174    }
15175
15176    #[test]
15177    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
15178        // `start : . EOF ;` on empty input. The wildcard is modeled as an
15179        // empty-complement not-set; at EOF the follow state (the explicit EOF
15180        // match) expects EOF, so even in the start rule recovery must perform
15181        // single-token insertion (`<missing ...>`) rather than aborting — matching
15182        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
15183        let atn = wildcard_then_eof_atn();
15184        let data = RecognizerData::new(
15185            "Mini.g4",
15186            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15187        );
15188        let mut parser = BaseParser::new(
15189            CommonTokenStream::new(Source {
15190                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
15191                index: 0,
15192            }),
15193            data,
15194        );
15195        parser.rule_context_stack = vec![RuleContextFrame {
15196            rule_index: 0,
15197            invoking_state: 0,
15198        }];
15199
15200        let node = parser
15201            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
15202            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
15203
15204        // A single `<missing ...>` error node is inserted; EOF is not consumed.
15205        assert_eq!(node.children().len(), 1);
15206        assert!(!node.consumed_eof());
15207        assert!(
15208            parser
15209                .node(node.children()[0])
15210                .text()
15211                .starts_with("<missing")
15212        );
15213        assert_eq!(parser.la(1), TOKEN_EOF);
15214        assert_eq!(
15215            parser.generated_parser_diagnostics,
15216            [ParserDiagnostic {
15217                line: 1,
15218                column: 1,
15219                message: "missing 'x' at '<EOF>'".to_owned(),
15220            }]
15221        );
15222    }
15223
15224    #[test]
15225    fn generated_rule_recovery_consumes_to_parent_follow() {
15226        let atn = generated_match_recovery_atn();
15227        let data = RecognizerData::new(
15228            "Mini.g4",
15229            Vocabulary::new(
15230                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15231                [None, Some("X"), Some("Y"), Some("Z")],
15232                [None::<&str>, None, None, None],
15233            ),
15234        );
15235        let mut parser = BaseParser::new(
15236            CommonTokenStream::new(Source {
15237                tokens: vec![
15238                    TestToken::new(3).with_text("z"),
15239                    TestToken::eof("parser-test", 1, 1, 1),
15240                ],
15241                index: 0,
15242            }),
15243            data,
15244        );
15245        let _parent = parser.enter_rule(0, 0);
15246        let marker = parser.push_invoking_state(1);
15247        let mut child = parser.enter_rule(4, 1);
15248        parser.discard_invoking_state(marker);
15249
15250        parser.recover_generated_rule(
15251            &mut child,
15252            &atn,
15253            AntlrError::ParserError {
15254                line: 1,
15255                column: 0,
15256                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15257            },
15258        );
15259        let tree = parser.finish_rule(child, false);
15260
15261        assert_eq!(parser.la(1), TOKEN_EOF);
15262        assert_eq!(
15263            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
15264            "(a z)"
15265        );
15266        assert_eq!(parser.number_of_syntax_errors(), 1);
15267        assert_eq!(
15268            parser.generated_parser_diagnostics,
15269            [ParserDiagnostic {
15270                line: 1,
15271                column: 0,
15272                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
15273            }]
15274        );
15275        parser.exit_rule();
15276    }
15277
15278    #[test]
15279    fn greedy_ll1_alt_handles_nullable_loop_exit() {
15280        let mut body_symbols = TokenBitSet::default();
15281        body_symbols.insert(1);
15282        let entry = DecisionLookahead {
15283            transitions: vec![
15284                TransitionLookSet {
15285                    symbols: body_symbols,
15286                    nullable: false,
15287                },
15288                TransitionLookSet {
15289                    symbols: TokenBitSet::default(),
15290                    nullable: true,
15291                },
15292            ],
15293        };
15294
15295        assert_eq!(ll1_unique_alt(&entry, 2), None);
15296        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
15297        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
15298        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
15299    }
15300
15301    #[test]
15302    fn ordinary_repetition_builds_tree_in_input_order() {
15303        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15304            let mut parser = mini_parser(repeated_x_tokens(3));
15305            let tree = parser
15306                .parse_atn_rule(&atn, 0)
15307                .expect("ordinary repetition should parse");
15308
15309            let root = parser
15310                .node(tree)
15311                .as_rule()
15312                .expect("entry result should be a rule");
15313            let body_rules = root.child_rules(1).collect::<Vec<_>>();
15314            assert_eq!(root.text(), "xxx<EOF>");
15315            assert_eq!(body_rules.len(), 3);
15316            assert_eq!(
15317                body_rules
15318                    .iter()
15319                    .map(|rule| rule.start_id().expect("body start").index())
15320                    .collect::<Vec<_>>(),
15321                [0, 1, 2]
15322            );
15323            assert_eq!(
15324                body_rules
15325                    .iter()
15326                    .map(|rule| rule.stop_id().expect("body stop").index())
15327                    .collect::<Vec<_>>(),
15328                [0, 1, 2]
15329            );
15330            assert_eq!(parser.number_of_syntax_errors(), 0);
15331        }
15332    }
15333
15334    #[test]
15335    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
15336        const DEPTH: usize = 20_000;
15337
15338        std::thread::Builder::new()
15339            .name("deferred-rule-materialization".to_owned())
15340            .stack_size(256 * 1024)
15341            .spawn(|| {
15342                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15343                let mut root = FastDeferredNodeId::EMPTY;
15344                for depth in 0..DEPTH {
15345                    root = parser
15346                        .recognition_arena
15347                        .deferred_rule_node(FastDeferredRule {
15348                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
15349                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
15350                            start_index: 0,
15351                            stop_index: None,
15352                            deferred_children: root,
15353                            children: NodeSeqId::EMPTY,
15354                        });
15355                }
15356
15357                let mut children = parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
15358                for expected_rule in (0..DEPTH).rev() {
15359                    let mut nodes = parser.recognition_arena.iter(children);
15360                    let node = nodes.next().expect("nested rule node");
15361                    assert!(nodes.next().is_none(), "each rule has one child");
15362                    let ArenaRecognizedNode::Rule {
15363                        rule_index,
15364                        children: nested,
15365                        ..
15366                    } = parser.recognition_arena.node(node)
15367                    else {
15368                        panic!("expected nested rule");
15369                    };
15370                    assert_eq!(rule_index as usize, expected_rule);
15371                    children = nested;
15372                }
15373                assert!(children.is_empty());
15374            })
15375            .expect("small-stack thread should start")
15376            .join()
15377            .expect("deferred rules should materialize without recursion");
15378    }
15379
15380    #[test]
15381    fn nested_rule_chain_with_adaptive_set_fits_default_stack() {
15382        // This depth separates the corrected frame from v0.14.0 on a 2 MiB stack.
15383        const DEPTH: usize = 130;
15384        const STACK_SIZE: usize = 2 * 1024 * 1024;
15385
15386        std::thread::Builder::new()
15387            .name("nested-adaptive-set-rules".to_owned())
15388            .stack_size(STACK_SIZE)
15389            .spawn(|| {
15390                let atn = nested_rule_chain_atn(DEPTH);
15391                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
15392                parser.set_build_parse_trees(false);
15393                parser
15394                    .parse_atn_rule(&atn, 0)
15395                    .expect("nested rule chain should parse on the default-sized stack");
15396                assert_eq!(parser.input.index(), 1);
15397            })
15398            .expect("small-stack thread should start")
15399            .join()
15400            .expect("nested rule chain should not overflow its stack");
15401    }
15402
15403    #[test]
15404    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
15405        const REPETITIONS: usize = 64;
15406
15407        let atn = ambiguous_ordinary_star_loop_atn();
15408        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15409        let tree = parser
15410            .parse_atn_rule(&atn, 0)
15411            .expect("ambiguous ordinary repetition should parse");
15412
15413        let root = parser
15414            .node(tree)
15415            .as_rule()
15416            .expect("entry result should be a rule");
15417        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
15418        assert_eq!(parser.input.index(), REPETITIONS);
15419        assert!(
15420            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
15421            "equivalent segmentations should keep deferred storage linear"
15422        );
15423        assert_eq!(parser.number_of_syntax_errors(), 0);
15424    }
15425
15426    #[test]
15427    fn long_ordinary_repetition_does_not_consume_native_stack() {
15428        const REPETITIONS: usize = 20_000;
15429
15430        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15431            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15432            parser.set_build_parse_trees(false);
15433            parser
15434                .parse_atn_rule(&atn, 0)
15435                .expect("long ordinary repetition should parse");
15436
15437            assert_eq!(parser.input.index(), REPETITIONS);
15438            assert_eq!(parser.number_of_syntax_errors(), 0);
15439        }
15440    }
15441
15442    #[test]
15443    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
15444        const REPETITIONS: usize = 2_000;
15445        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
15446
15447        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
15448            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
15449            let tree = parser
15450                .parse_atn_rule(&atn, 0)
15451                .expect("long rule repetition should parse");
15452
15453            let root = parser
15454                .node(tree)
15455                .as_rule()
15456                .expect("entry result should be a rule");
15457            assert_eq!(root.text(), expected_text);
15458            assert_eq!(root.child_rules(1).count(), REPETITIONS);
15459            let first_body = root.child_rules(1).next().expect("first body rule");
15460            let last_body = root.child_rules(1).next_back().expect("last body rule");
15461            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
15462            assert_eq!(
15463                last_body.stop_id().expect("last body stop").index(),
15464                REPETITIONS - 1
15465            );
15466
15467            let stats = parser.recognition_arena_stats();
15468            assert_eq!(
15469                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
15470                (REPETITIONS, REPETITIONS, 0)
15471            );
15472            assert_eq!(
15473                (stats.total_links, stats.live_links, stats.dead_links),
15474                (REPETITIONS, REPETITIONS, 0)
15475            );
15476            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
15477            assert_eq!(
15478                parser.recognition_arena.deferred_nodes.len(),
15479                REPETITIONS * 2 - 1
15480            );
15481            assert_eq!(parser.number_of_syntax_errors(), 0);
15482        }
15483    }
15484
15485    #[test]
15486    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
15487        let key = |state_number| FastRecognizeKey {
15488            state_number,
15489            stop_state: 10,
15490            index: state_number,
15491            rule_start_index: 0,
15492            decision_start_index: None,
15493            precedence: 0,
15494            recovery_symbols_id: 0,
15495            recovery_state: None,
15496        };
15497
15498        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15499        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
15500            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
15501        }
15502        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
15503        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
15504
15505        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15506        let repeated = key(1);
15507        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
15508            assert!(promote.clean_memo_enabled_for_key(&repeated));
15509        }
15510        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
15511
15512        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
15513            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
15514        }
15515        assert!(sparse.clean_memo_enabled_for_key(&repeated));
15516        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
15517        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
15518            assert!(sparse.clean_memo_enabled_for_key(&repeated));
15519        }
15520        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
15521    }
15522
15523    #[test]
15524    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
15525        assert_eq!(
15526            fast_recognize_memo_capacity(0),
15527            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15528        );
15529        assert_eq!(
15530            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
15531            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
15532        );
15533        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
15534        assert_eq!(
15535            fast_recognize_memo_capacity(usize::MAX),
15536            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
15537        );
15538    }
15539
15540    #[test]
15541    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
15542        let mut scratch = FastRecognizeTopScratch::default();
15543        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15544        let retained_capacity = scratch.memo.capacity();
15545        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
15546        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15547
15548        let larger_capacity = retained_capacity + 1;
15549        scratch.prepare(larger_capacity);
15550        let grown_capacity = scratch.memo.capacity();
15551        assert!(grown_capacity >= larger_capacity);
15552        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15553
15554        scratch.memo.insert(
15555            FastRecognizeKey {
15556                state_number: 0,
15557                stop_state: 0,
15558                index: 0,
15559                rule_start_index: 0,
15560                decision_start_index: None,
15561                precedence: 0,
15562                recovery_symbols_id: 0,
15563                recovery_state: None,
15564            },
15565            Rc::from([FastRecognizeOutcome {
15566                index: 0,
15567                consumed_eof: false,
15568                diagnostics: DiagnosticSeqId::EMPTY,
15569                deferred_nodes: FastDeferredNodeId::EMPTY,
15570                nodes: NodeSeqId::EMPTY,
15571            }]),
15572        );
15573        scratch.release_oversized_memo();
15574        assert!(scratch.memo.is_empty());
15575        assert_eq!(scratch.memo.capacity(), grown_capacity);
15576
15577        scratch
15578            .memo
15579            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
15580        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
15581
15582        scratch.release_oversized_memo();
15583        assert!(scratch.memo.is_empty());
15584        assert_eq!(scratch.memo.capacity(), 0);
15585    }
15586
15587    #[test]
15588    fn clean_empty_multi_alt_outcomes_are_memoized() {
15589        let mut atn = ParserAtnBuilder::new(2);
15590        assert_eq!(
15591            atn.add_state(AtnStateKind::RuleStart, Some(0))
15592                .expect("state")
15593                .index(),
15594            0
15595        );
15596        assert_eq!(
15597            atn.add_state(AtnStateKind::BlockStart, Some(0))
15598                .expect("state")
15599                .index(),
15600            1
15601        );
15602        assert_eq!(
15603            atn.add_state(AtnStateKind::RuleStop, Some(0))
15604                .expect("state")
15605                .index(),
15606            2
15607        );
15608        atn.set_rule_to_start_state(vec![0])
15609            .expect("rule start states");
15610        atn.set_rule_to_stop_state(vec![2])
15611            .expect("rule stop states");
15612        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15613            .expect("transition");
15614        atn.add_transition(
15615            1,
15616            ParserTransitionSpec::Atom {
15617                target: 2,
15618                label: 1,
15619            },
15620        )
15621        .expect("transition");
15622        atn.add_transition(
15623            1,
15624            ParserTransitionSpec::Atom {
15625                target: 2,
15626                label: 2,
15627            },
15628        )
15629        .expect("transition");
15630        let atn = finish_atn(atn);
15631
15632        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
15633        parser.fast_recovery_enabled = false;
15634        let mut visiting = FxHashSet::default();
15635        let mut memo = FxHashMap::default();
15636        let mut expected = ExpectedTokens::default();
15637        let outcomes = parser.recognize_state_fast(
15638            &atn,
15639            FastRecognizeRequest {
15640                state_number: 1,
15641                stop_state: 2,
15642                index: 0,
15643                rule_start_index: 0,
15644                decision_start_index: None,
15645                precedence: 0,
15646                depth: 0,
15647                recovery_symbols: parser.empty_recovery_symbols(),
15648                recovery_state: None,
15649            },
15650            FastRecognizeScratch {
15651                predicate_context: None,
15652                visiting: &mut visiting,
15653                memo: &mut memo,
15654                expected: &mut expected,
15655            },
15656        );
15657
15658        assert!(outcomes.is_empty());
15659        assert_eq!(memo.len(), 1);
15660        assert!(memo.values().next().expect("memo entry").is_empty());
15661
15662        parser.clean_memo_mode = CleanMemoMode::Sparse;
15663        visiting.clear();
15664        memo.clear();
15665        expected = ExpectedTokens::default();
15666        let sparse_outcomes = parser.recognize_state_fast(
15667            &atn,
15668            FastRecognizeRequest {
15669                state_number: 1,
15670                stop_state: 2,
15671                index: 0,
15672                rule_start_index: 0,
15673                decision_start_index: None,
15674                precedence: 0,
15675                depth: 0,
15676                recovery_symbols: parser.empty_recovery_symbols(),
15677                recovery_state: None,
15678            },
15679            FastRecognizeScratch {
15680                predicate_context: None,
15681                visiting: &mut visiting,
15682                memo: &mut memo,
15683                expected: &mut expected,
15684            },
15685        );
15686
15687        assert!(sparse_outcomes.is_empty());
15688        assert!(memo.is_empty());
15689    }
15690
15691    #[test]
15692    fn wildcard_matches_non_eof_only() {
15693        let mut parser = mini_parser(vec![
15694            TestToken::new(1).with_text("x"),
15695            TestToken::eof("parser-test", 1, 1, 1),
15696        ]);
15697        let matched = parser.match_wildcard().expect("wildcard");
15698        assert_eq!(parser.node(matched).text(), "x");
15699        assert!(parser.match_wildcard().is_err());
15700    }
15701
15702    #[test]
15703    fn add_parse_child_records_match_even_without_tree_building() {
15704        // `sync_decision`'s "is the current context empty" flag must reflect real
15705        // matches, not parse-tree children: when `build_parse_trees(false)`,
15706        // `children` stays empty but `has_matched_child` must still flip so nested
15707        // recovery does not wrongly suppress single-token deletion.
15708        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15709        let token = TestToken::new(1).with_text("x");
15710
15711        parser.set_build_parse_trees(false);
15712        let mut ctx = ParserRuleContext::new(0, 0);
15713        assert!(!ctx.has_matched_child());
15714        let child = parser.terminal_tree(token.id);
15715        parser.add_parse_child(&mut ctx, child);
15716        // Tree building is off, so no child is stored...
15717        assert_eq!(ctx.child_count(), 0);
15718        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15719        // ...but the match is recorded, so the context is no longer "empty".
15720        assert!(ctx.has_matched_child());
15721
15722        // With tree building on, the child is stored and the match is recorded.
15723        parser.set_build_parse_trees(true);
15724        let mut ctx = ParserRuleContext::new(0, 0);
15725        let child = parser.terminal_tree(token.id);
15726        parser.add_parse_child(&mut ctx, child);
15727        assert_eq!(ctx.child_count(), 1);
15728        assert!(ctx.has_matched_child());
15729    }
15730
15731    #[test]
15732    fn disabled_tree_building_does_not_grow_flat_storage() {
15733        let mut parser = mini_parser(vec![
15734            TestToken::new(1).with_text("x"),
15735            TestToken::new(1).with_text("y"),
15736            TestToken::eof("parser-test", 2, 1, 2),
15737        ]);
15738        parser.set_build_parse_trees(false);
15739        let mut context = ParserRuleContext::new(0, -1);
15740
15741        for _ in 0..2 {
15742            let child = parser.match_token(1).expect("token should match");
15743            parser.add_parse_child(&mut context, child);
15744        }
15745        let current = parser.input.lt_id(1).expect("EOF token");
15746        let error = parser.error_tree(current);
15747        parser.add_parse_child(&mut context, error);
15748        let root = parser.rule_node(context);
15749
15750        assert_eq!(
15751            parser.parse_tree_storage().stats(),
15752            ParseTreeStats::default()
15753        );
15754        assert!(
15755            parser
15756                .parse_tree_storage()
15757                .node(parser.token_store(), root)
15758                .is_none(),
15759            "the no-tree sentinel must not resolve to stored data"
15760        );
15761    }
15762
15763    #[test]
15764    fn disabled_tree_building_skips_recognition_rule_node_storage() {
15765        let atn = ordinary_star_loop_atn();
15766        let mut parser = mini_parser(repeated_x_tokens(3));
15767        parser.set_build_parse_trees(false);
15768
15769        parser
15770            .parse_atn_rule(&atn, 0)
15771            .expect("ordinary repetition should parse without a tree");
15772
15773        assert_eq!(parser.input.index(), 3);
15774        assert!(parser.recognition_arena.nodes.is_empty());
15775        assert!(parser.recognition_arena.seq_links.is_empty());
15776        assert!(parser.recognition_arena.deferred_nodes.is_empty());
15777        assert!(parser.recognition_arena.deferred_rules.is_empty());
15778        assert!(!parser.fast_token_nodes_enabled);
15779        assert!(parser.fast_recognize_scratch.memo.is_empty());
15780    }
15781
15782    #[test]
15783    fn parser_interprets_simple_atn_rule() {
15784        let atn = token_then_eof_atn();
15785        let mut parser = mini_parser(vec![
15786            TestToken::new(1).with_text("x"),
15787            TestToken::eof("parser-test", 1, 1, 1),
15788        ]);
15789
15790        let tree = parser
15791            .parse_atn_rule(&atn, 0)
15792            .expect("artificial parser rule should parse");
15793        assert_eq!(parser.node(tree).text(), "x<EOF>");
15794        assert_eq!(parser.number_of_syntax_errors(), 0);
15795        assert_eq!(
15796            parser
15797                .node(tree)
15798                .first_rule_stop(0)
15799                .expect("rule should stop at EOF")
15800                .token_type(),
15801            TOKEN_EOF
15802        );
15803
15804        let mut parser = mini_parser(vec![
15805            TestToken::new(1).with_text("x"),
15806            TestToken::eof("parser-test", 1, 1, 1),
15807        ]);
15808        let (tree, actions) = parser
15809            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15810            .expect("runtime-option parser rule should parse");
15811        assert!(actions.is_empty());
15812        assert_eq!(
15813            parser
15814                .node(tree)
15815                .first_rule_stop(0)
15816                .expect("rule should stop at EOF")
15817                .token_type(),
15818            TOKEN_EOF
15819        );
15820    }
15821
15822    #[test]
15823    fn runtime_options_default_ignores_noop_action_transitions() {
15824        let atn = noop_action_then_token_then_eof_atn();
15825        let mut parser = mini_parser(vec![
15826            TestToken::new(1).with_text("x"),
15827            TestToken::eof("parser-test", 1, 1, 1),
15828        ]);
15829
15830        let (tree, actions) = parser
15831            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15832            .expect("no-op parser action should not force action replay");
15833
15834        assert_eq!(parser.node(tree).text(), "x<EOF>");
15835        assert!(
15836            actions.is_empty(),
15837            "action_index=None transitions are ANTLR metadata, not replay actions"
15838        );
15839        assert_eq!(parser.number_of_syntax_errors(), 0);
15840    }
15841
15842    #[test]
15843    fn parser_exposes_buffered_token_stream_after_parse() {
15844        let atn = token_then_eof_atn();
15845        let mut parser = mini_parser(vec![
15846            TestToken::new(1).with_text("x"),
15847            TestToken::eof("parser-test", 1, 1, 1),
15848        ]);
15849
15850        let tree = parser
15851            .parse_atn_rule(&atn, 0)
15852            .expect("artificial parser rule should parse");
15853        assert_eq!(parser.node(tree).text(), "x<EOF>");
15854
15855        let stream = parser.token_stream();
15856        let source_index_after_parse = stream.token_source().index;
15857        let buffered = stream.tokens().collect::<Vec<_>>();
15858        assert_eq!(buffered.len(), 2);
15859        assert_eq!(buffered[0].text(), "x");
15860        assert_eq!(buffered[0].token_id().index(), 0);
15861        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
15862        assert_eq!(stream.token_source().index, source_index_after_parse);
15863        drop(buffered);
15864
15865        let stream = parser.into_token_stream();
15866        assert_eq!(stream.token_source().index, source_index_after_parse);
15867        assert_eq!(stream.tokens().next().expect("first token").text(), "x");
15868        assert_eq!(
15869            stream.tokens().nth(1).expect("EOF token").token_type(),
15870            TOKEN_EOF
15871        );
15872    }
15873
15874    #[test]
15875    fn parser_syntax_error_count_tracks_interpreted_recovery() {
15876        let atn = token_then_eof_atn();
15877        let mut parser = mini_parser(vec![
15878            TestToken::new(1).with_text("x"),
15879            TestToken::new(2).with_text("y"),
15880            TestToken::eof("parser-test", 2, 1, 2),
15881        ]);
15882
15883        let tree = parser
15884            .parse_atn_rule(&atn, 0)
15885            .expect("invalid token should recover into an error node");
15886
15887        assert_eq!(parser.number_of_syntax_errors(), 1);
15888        assert_eq!(
15889            parser
15890                .node(tree)
15891                .first_error_token()
15892                .expect("recovery should embed an error token")
15893                .text(),
15894            "y"
15895        );
15896    }
15897
15898    #[test]
15899    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
15900        let atn = token_then_eof_atn();
15901        let mut parser = mini_parser(vec![
15902            TestToken::new(2).with_text("y"),
15903            TestToken::eof("parser-test", 1, 1, 1),
15904        ]);
15905
15906        let error = parser
15907            .parse_atn_rule(&atn, 0)
15908            .expect_err("start-rule mismatch should remain a parser error");
15909
15910        assert_eq!(parser.number_of_syntax_errors(), 1);
15911        assert!(matches!(error, AntlrError::ParserError { .. }));
15912    }
15913
15914    #[test]
15915    fn adaptive_direct_rule_uses_simulator_decision() {
15916        let atn = two_alt_decision_atn();
15917        let mut simulator = ParserAtnSimulator::new(&atn);
15918        let mut parser = mini_parser(vec![
15919            TestToken::new(2).with_text("y"),
15920            TestToken::eof("parser-test", 1, 1, 1),
15921        ]);
15922
15923        let tree = parser
15924            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15925            .expect("direct adaptive rule should parse");
15926
15927        assert_eq!(parser.node(tree).text(), "y");
15928        assert_eq!(parser.input.index(), 1);
15929    }
15930
15931    #[test]
15932    fn adaptive_direct_rule_restores_input_on_fallback() {
15933        let atn = predicate_after_token_atn();
15934        let mut simulator = ParserAtnSimulator::new(&atn);
15935        let mut parser = mini_parser(vec![
15936            TestToken::new(1).with_text("x"),
15937            TestToken::new(2).with_text("y"),
15938            TestToken::eof("parser-test", 2, 1, 2),
15939        ]);
15940
15941        let tree = parser
15942            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
15943            .expect("fallback recognizer should parse");
15944
15945        assert_eq!(parser.node(tree).text(), "xy");
15946        assert_eq!(parser.input.index(), 2);
15947        let stats = parser.parse_tree_storage().stats();
15948        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
15949        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
15950        assert_eq!(stats.scratch_links, 0);
15951    }
15952
15953    #[test]
15954    fn unknown_predicate_policy_defaults_to_assume_true() {
15955        let atn = predicate_after_token_atn();
15956        let mut parser = mini_parser(vec![
15957            TestToken::new(1).with_text("x"),
15958            TestToken::new(2).with_text("y"),
15959            TestToken::eof("parser-test", 2, 1, 2),
15960        ]);
15961
15962        let (tree, _) = parser
15963            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15964            .expect("unknown predicate should pass under the default policy");
15965
15966        assert_eq!(parser.node(tree).text(), "xy");
15967        assert_eq!(parser.number_of_syntax_errors(), 0);
15968    }
15969
15970    #[test]
15971    fn predicate_gated_same_lookahead_uses_viable_alternative() {
15972        let atn = predicate_gated_same_lookahead_atn([0, 1]);
15973        let mut parser = mini_parser(vec![
15974            TestToken::new(1).with_text("x"),
15975            TestToken::eof("parser-test", 1, 1, 1),
15976        ]);
15977
15978        let (tree, _) = parser
15979            .parse_atn_rule_with_runtime_options(
15980                &atn,
15981                0,
15982                ParserRuntimeOptions {
15983                    predicates: &[
15984                        (0, 0, ParserPredicate::False),
15985                        (0, 1, ParserPredicate::True),
15986                    ],
15987                    ..ParserRuntimeOptions::default()
15988                },
15989            )
15990            .expect("the second predicate-gated alternative should match");
15991
15992        assert_eq!(parser.node(tree).text(), "x<EOF>");
15993        assert_eq!(parser.number_of_syntax_errors(), 0);
15994        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
15995        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
15996    }
15997
15998    #[test]
15999    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
16000        // A generated parent may record an unknown-predicate coordinate, then
16001        // descend into an interpreted child. The child's interpreter entry must
16002        // not wipe the parent's recorded hit before the top-level surfaces it.
16003        let atn = token_then_eof_atn();
16004        let mut parser = mini_parser(vec![
16005            TestToken::new(1).with_text("x"),
16006            TestToken::eof("parser-test", 1, 1, 1),
16007        ]);
16008
16009        // Simulate the parent having recorded a fail-loud coordinate.
16010        parser.unknown_predicate_hits.push((7, 3));
16011
16012        // Run an interpreted child parse that records no coordinate of its own.
16013        parser
16014            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16015            .expect("child rule parses");
16016
16017        // The parent's coordinate must still be present for the top-level entry.
16018        let error = parser
16019            .take_unknown_semantic_error()
16020            .expect("parent's recorded coordinate must survive the nested interpreted parse");
16021        let AntlrError::Unsupported(message) = error else {
16022            panic!("expected AntlrError::Unsupported, got {error:?}");
16023        };
16024        assert!(message.contains("pred_index=3"), "message: {message}");
16025    }
16026
16027    #[test]
16028    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
16029        let atn = predicate_after_token_atn();
16030        let mut parser = mini_parser(vec![
16031            TestToken::new(1).with_text("x"),
16032            TestToken::new(2).with_text("y"),
16033            TestToken::eof("parser-test", 2, 1, 2),
16034        ]);
16035
16036        let result = parser.parse_atn_rule_with_runtime_options(
16037            &atn,
16038            0,
16039            ParserRuntimeOptions {
16040                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16041                ..ParserRuntimeOptions::default()
16042            },
16043        );
16044
16045        assert!(
16046            result.is_err(),
16047            "the only path is predicate-guarded, so assume-false must fail the parse"
16048        );
16049    }
16050
16051    #[test]
16052    fn predicate_failure_message_keeps_semantic_recovery_path() {
16053        let atn = predicate_after_token_atn();
16054        let mut parser = mini_parser(vec![
16055            TestToken::new(1).with_text("x"),
16056            TestToken::new(2).with_text("y"),
16057            TestToken::eof("parser-test", 2, 1, 2),
16058        ]);
16059
16060        let (tree, _) = parser
16061            .parse_atn_rule_with_runtime_options(
16062                &atn,
16063                0,
16064                ParserRuntimeOptions {
16065                    predicates: &[(
16066                        0,
16067                        0,
16068                        ParserPredicate::FalseWithMessage {
16069                            message: "predicate rejected input",
16070                        },
16071                    )],
16072                    ..ParserRuntimeOptions::default()
16073                },
16074            )
16075            .expect("failure-message predicates recover through the semantic interpreter");
16076
16077        assert_eq!(parser.node(tree).text(), "xy");
16078        assert_eq!(parser.number_of_syntax_errors(), 1);
16079        assert!(
16080            parser.fast_predicate_cache.is_empty(),
16081            "failure-message predicates need the semantic interpreter's recovery outcome"
16082        );
16083    }
16084
16085    #[test]
16086    fn unknown_predicate_policy_error_names_the_coordinate() {
16087        let atn = predicate_after_token_atn();
16088        let mut parser = mini_parser(vec![
16089            TestToken::new(1).with_text("x"),
16090            TestToken::new(2).with_text("y"),
16091            TestToken::eof("parser-test", 2, 1, 2),
16092        ]);
16093
16094        let error = parser
16095            .parse_atn_rule_with_runtime_options(
16096                &atn,
16097                0,
16098                ParserRuntimeOptions {
16099                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16100                    ..ParserRuntimeOptions::default()
16101                },
16102            )
16103            .expect_err("evaluating an unknown predicate under Error policy must fail");
16104
16105        let AntlrError::Unsupported(message) = error else {
16106            panic!("expected AntlrError::Unsupported, got {error:?}");
16107        };
16108        assert!(
16109            message.contains("unsupported semantic predicate"),
16110            "message should name the failure class: {message}"
16111        );
16112        assert!(
16113            message.contains("pred_index=0"),
16114            "message should carry the coordinate: {message}"
16115        );
16116    }
16117
16118    #[test]
16119    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
16120        // A parser reused after a fail-loud parse must not carry the old
16121        // coordinates into a later parse. The fail-loud return keeps the hits
16122        // (so a generated parent can surface a recovered child's coordinate),
16123        // and the next parse's entry stashes/replaces them, so a subsequent
16124        // clean parse surfaces no stale error.
16125        let atn = predicate_after_token_atn();
16126        let mut parser = mini_parser(vec![
16127            TestToken::new(1).with_text("x"),
16128            TestToken::new(2).with_text("y"),
16129            TestToken::eof("parser-test", 2, 1, 2),
16130        ]);
16131
16132        parser
16133            .parse_atn_rule_with_runtime_options(
16134                &atn,
16135                0,
16136                ParserRuntimeOptions {
16137                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16138                    ..ParserRuntimeOptions::default()
16139                },
16140            )
16141            .expect_err("first parse fails loud under the Error policy");
16142
16143        // The failed parse kept its coordinate on the parser (so a generated
16144        // parent could surface a recovered child). A top-level reuse resets the
16145        // hits — generated parsers call `reset_unknown_semantic_hits` at their
16146        // public entry; direct interpreter-API callers do the same.
16147        parser.reset_unknown_semantic_hits();
16148        assert!(
16149            parser.take_unknown_semantic_error().is_none(),
16150            "reset must drop stale unknown-predicate coordinates before a reused parse"
16151        );
16152    }
16153
16154    #[derive(Debug, Default)]
16155    struct RecordingHooks {
16156        predicates: Vec<(usize, usize, usize, Option<String>)>,
16157        actions: Vec<(usize, String, Option<String>)>,
16158        action_trees: Vec<Option<String>>,
16159    }
16160
16161    impl SemanticHooks for RecordingHooks {
16162        fn sempred<S>(
16163            &mut self,
16164            ctx: &mut ParserSemCtx<'_, S>,
16165            rule_index: usize,
16166            pred_index: usize,
16167        ) -> Option<bool>
16168        where
16169            S: TokenSource,
16170        {
16171            self.predicates.push((
16172                ctx.input_index(),
16173                rule_index,
16174                pred_index,
16175                ctx.token_text(1).map(|token| token.text().to_owned()),
16176            ));
16177            Some(true)
16178        }
16179
16180        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
16181        where
16182            S: TokenSource,
16183        {
16184            self.actions.push((
16185                action.source_state(),
16186                ctx.action_text(),
16187                ctx.rule_name().map(str::to_owned),
16188            ));
16189            self.action_trees.push(ctx.tree().map(Node::text));
16190            true
16191        }
16192    }
16193
16194    #[derive(Debug, Default)]
16195    struct RejectingPredicateHooks {
16196        predicates: Vec<(usize, usize, usize, Option<String>)>,
16197    }
16198
16199    impl SemanticHooks for RejectingPredicateHooks {
16200        fn sempred<S>(
16201            &mut self,
16202            ctx: &mut ParserSemCtx<'_, S>,
16203            rule_index: usize,
16204            pred_index: usize,
16205        ) -> Option<bool>
16206        where
16207            S: TokenSource,
16208        {
16209            self.predicates.push((
16210                ctx.input_index(),
16211                rule_index,
16212                pred_index,
16213                ctx.token_text(1).map(|token| token.text().to_owned()),
16214            ));
16215            Some(false)
16216        }
16217    }
16218
16219    #[test]
16220    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
16221        let atn = predicate_gated_same_lookahead_atn([0, 0]);
16222        let mut parser = mini_parser_with_hooks(
16223            vec![
16224                TestToken::new(1).with_text("x"),
16225                TestToken::eof("parser-test", 1, 1, 1),
16226            ],
16227            RecordingHooks::default(),
16228        );
16229
16230        let (tree, _) = parser
16231            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16232            .expect("both alternatives share one replay-safe predicate result");
16233
16234        assert_eq!(parser.node(tree).text(), "x<EOF>");
16235        assert_eq!(
16236            parser.semantic_hooks.predicates,
16237            vec![(0, 0, 0, Some("x".to_owned()))]
16238        );
16239        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
16240    }
16241
16242    #[test]
16243    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
16244        let atn = predicate_after_token_atn();
16245        let mut parser = mini_parser_with_hooks(
16246            vec![
16247                TestToken::new(1).with_text("x"),
16248                TestToken::new(2).with_text("y"),
16249                TestToken::eof("parser-test", 2, 1, 2),
16250            ],
16251            RecordingHooks::default(),
16252        );
16253
16254        let (tree, _) = parser
16255            .parse_atn_rule_with_runtime_options(
16256                &atn,
16257                0,
16258                ParserRuntimeOptions {
16259                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16260                    ..ParserRuntimeOptions::default()
16261                },
16262            )
16263            .expect("hook supplies the missing predicate result");
16264
16265        assert_eq!(parser.node(tree).text(), "xy");
16266        assert_eq!(
16267            parser.semantic_hooks.predicates,
16268            vec![(1, 0, 0, Some("y".to_owned()))]
16269        );
16270        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
16271    }
16272
16273    #[test]
16274    fn runtime_options_default_preserves_semantic_hook_predicates() {
16275        let atn = predicate_after_token_atn();
16276        let mut parser = mini_parser_with_hooks(
16277            vec![
16278                TestToken::new(1).with_text("x"),
16279                TestToken::new(2).with_text("y"),
16280                TestToken::eof("parser-test", 2, 1, 2),
16281            ],
16282            RejectingPredicateHooks::default(),
16283        );
16284
16285        let result =
16286            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
16287
16288        assert!(
16289            result.is_err(),
16290            "default runtime options must not bypass semantic hooks for predicate ATNs"
16291        );
16292        assert_eq!(
16293            parser.semantic_hooks.predicates,
16294            vec![(1, 0, 0, Some("y".to_owned()))]
16295        );
16296        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
16297    }
16298
16299    #[test]
16300    fn semantic_hook_handles_committed_parser_action() {
16301        let atn = token_then_eof_atn();
16302        let mut parser = mini_parser_with_hooks(
16303            vec![
16304                TestToken::new(1).with_text("x"),
16305                TestToken::eof("parser-test", 1, 1, 1),
16306            ],
16307            RecordingHooks::default(),
16308        );
16309        let (tree, _) = parser
16310            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16311            .expect("rule parses before action hook is tested");
16312
16313        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16314        assert_eq!(
16315            parser.semantic_hooks.actions,
16316            vec![(42, "x".to_owned(), Some("s".to_owned()))]
16317        );
16318        assert_eq!(
16319            parser.semantic_hooks.action_trees,
16320            [Some("x<EOF>".to_owned())]
16321        );
16322    }
16323
16324    #[test]
16325    fn unhandled_committed_action_fails_loud_under_error_policy() {
16326        // An action offered to the hook that no hook handles (returns false)
16327        // must be recorded and surfaced as `AntlrError::Unsupported` under the
16328        // Error policy, so a `hook`-disposed action is not silently dropped.
16329        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16330        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16331        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
16332
16333        // DecliningHooks::action returns false (unhandled).
16334        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16335
16336        let error = parser
16337            .take_unknown_semantic_error()
16338            .expect("an unhandled committed action under Error policy must fail loud");
16339        let AntlrError::Unsupported(message) = error else {
16340            panic!("expected AntlrError::Unsupported, got {error:?}");
16341        };
16342        assert!(
16343            message.contains("unhandled semantic action") && message.contains("state=42"),
16344            "message should name the dropped action coordinate: {message}"
16345        );
16346
16347        // Under the default (assume-true) policy the same miss is not recorded.
16348        let mut lenient =
16349            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16350        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
16351        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
16352        assert!(lenient.take_unknown_semantic_error().is_none());
16353    }
16354
16355    #[test]
16356    fn translated_predicate_is_unaffected_by_error_policy() {
16357        let atn = predicate_after_token_atn();
16358        let mut parser = mini_parser(vec![
16359            TestToken::new(1).with_text("x"),
16360            TestToken::new(2).with_text("y"),
16361            TestToken::eof("parser-test", 2, 1, 2),
16362        ]);
16363
16364        let (tree, _) = parser
16365            .parse_atn_rule_with_runtime_options(
16366                &atn,
16367                0,
16368                ParserRuntimeOptions {
16369                    predicates: &[(0, 0, ParserPredicate::True)],
16370                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16371                    ..ParserRuntimeOptions::default()
16372                },
16373            )
16374            .expect("a predicate covered by the table is not an unknown coordinate");
16375
16376        assert_eq!(parser.node(tree).text(), "xy");
16377    }
16378
16379    /// Hooks that decline (`None`) must fall through to the configured policy
16380    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
16381    /// legacy table path. Regression for the `unwrap_or(false)` that silently
16382    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
16383    fn hook_predicate_semantics() -> ParserSemantics {
16384        let mut ir = SemIr::new();
16385        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
16386        ParserSemantics {
16387            ir,
16388            predicates: vec![ParserSemanticPredicate {
16389                rule_index: 0,
16390                pred_index: 0,
16391                expr,
16392                failure_message: None,
16393            }],
16394            actions: Vec::new(),
16395        }
16396    }
16397
16398    #[derive(Debug, Default)]
16399    struct DecliningHooks;
16400
16401    impl SemanticHooks for DecliningHooks {}
16402
16403    #[test]
16404    fn semir_hook_none_falls_through_to_assume_true() {
16405        let atn = predicate_after_token_atn();
16406        let semantics = hook_predicate_semantics();
16407        let mut parser = mini_parser_with_hooks(
16408            vec![
16409                TestToken::new(1).with_text("x"),
16410                TestToken::new(2).with_text("y"),
16411                TestToken::eof("parser-test", 2, 1, 2),
16412            ],
16413            DecliningHooks,
16414        );
16415
16416        let (tree, _) = parser
16417            .parse_atn_rule_with_runtime_options(
16418                &atn,
16419                0,
16420                ParserRuntimeOptions {
16421                    semantics: Some(&semantics),
16422                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
16423                    ..ParserRuntimeOptions::default()
16424                },
16425            )
16426            .expect("a declined SemIR hook must pass under assume-true");
16427
16428        assert_eq!(parser.node(tree).text(), "xy");
16429    }
16430
16431    #[test]
16432    fn semir_hook_none_falls_through_to_assume_false() {
16433        let atn = predicate_after_token_atn();
16434        let semantics = hook_predicate_semantics();
16435        let mut parser = mini_parser_with_hooks(
16436            vec![
16437                TestToken::new(1).with_text("x"),
16438                TestToken::new(2).with_text("y"),
16439                TestToken::eof("parser-test", 2, 1, 2),
16440            ],
16441            DecliningHooks,
16442        );
16443
16444        let result = parser.parse_atn_rule_with_runtime_options(
16445            &atn,
16446            0,
16447            ParserRuntimeOptions {
16448                semantics: Some(&semantics),
16449                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
16450                ..ParserRuntimeOptions::default()
16451            },
16452        );
16453
16454        assert!(
16455            result.is_err(),
16456            "a declined SemIR hook must fail the only guarded path under assume-false"
16457        );
16458    }
16459
16460    #[test]
16461    fn semir_hook_none_records_coordinate_under_error_policy() {
16462        let atn = predicate_after_token_atn();
16463        let semantics = hook_predicate_semantics();
16464        let mut parser = mini_parser_with_hooks(
16465            vec![
16466                TestToken::new(1).with_text("x"),
16467                TestToken::new(2).with_text("y"),
16468                TestToken::eof("parser-test", 2, 1, 2),
16469            ],
16470            DecliningHooks,
16471        );
16472
16473        let error = parser
16474            .parse_atn_rule_with_runtime_options(
16475                &atn,
16476                0,
16477                ParserRuntimeOptions {
16478                    semantics: Some(&semantics),
16479                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
16480                    ..ParserRuntimeOptions::default()
16481                },
16482            )
16483            .expect_err("a declined SemIR hook under Error policy must fail the parse");
16484
16485        let AntlrError::Unsupported(message) = error else {
16486            panic!("expected AntlrError::Unsupported, got {error:?}");
16487        };
16488        assert!(
16489            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
16490            "message should name the unresolved coordinate: {message}"
16491        );
16492    }
16493
16494    #[test]
16495    fn generated_direct_predicate_honors_installed_policy() {
16496        // The generated recursive-descent path calls
16497        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
16498        // going through `ParserRuntimeOptions`, so the policy must be installed
16499        // via `set_unknown_predicate_policy` (as the generated constructor now
16500        // does). A declining hook must then honor it rather than the default.
16501        let semantics = hook_predicate_semantics();
16502        let context = ParserRuleContext::new(0, -1);
16503
16504        let mut assume_true =
16505            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16506        assert!(
16507            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
16508                &semantics, 0, 0, &context, 0
16509            ),
16510            "default AssumeTrue accepts a declined hook"
16511        );
16512        assert!(assume_true.take_unknown_semantic_error().is_none());
16513
16514        let mut error_policy =
16515            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
16516        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
16517        assert!(
16518            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
16519                &semantics, 0, 0, &context, 0
16520            ),
16521            "Error policy rejects a declined hook on the generated-direct path"
16522        );
16523        let error = error_policy
16524            .take_unknown_semantic_error()
16525            .expect("Error policy records the unresolved coordinate for the generated path");
16526        let AntlrError::Unsupported(message) = error else {
16527            panic!("expected AntlrError::Unsupported, got {error:?}");
16528        };
16529        assert!(message.contains("pred_index=0"), "message: {message}");
16530    }
16531
16532    #[test]
16533    fn parser_rule_start_skips_leading_hidden_tokens() {
16534        let atn = token_then_eof_atn();
16535        let mut parser = mini_parser(vec![
16536            TestToken::new(99)
16537                .with_text(" ")
16538                .with_channel(HIDDEN_CHANNEL),
16539            TestToken::new(1).with_text("x"),
16540            TestToken::eof("parser-test", 2, 1, 2),
16541        ]);
16542
16543        let tree = parser
16544            .parse_atn_rule(&atn, 0)
16545            .expect("artificial parser rule should parse");
16546        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
16547            panic!("rule node should be present");
16548        };
16549        assert_eq!(
16550            rule.start()
16551                .expect("rule should have a start token")
16552                .token_type(),
16553            1
16554        );
16555    }
16556
16557    #[test]
16558    fn parser_action_after_eof_stops_at_eof_token() {
16559        let atn = eof_then_action_atn();
16560        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16561
16562        let (_, actions) = parser
16563            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
16564            .expect("EOF action rule should parse");
16565
16566        assert_eq!(actions.len(), 1);
16567        assert_eq!(actions[0].stop_index(), Some(0));
16568        assert_eq!(
16569            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
16570            ""
16571        );
16572    }
16573
16574    #[test]
16575    fn after_action_stop_uses_rule_context_stop_not_cursor() {
16576        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
16577        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
16578        // but the rule did not consume it. The @after stop must follow the rule
16579        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
16580        let mut id = TestToken::new(1).with_text("x");
16581        id.set_token_index(0);
16582        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
16583        eof.set_token_index(1);
16584        let mut parser = mini_parser(vec![id.clone(), eof]);
16585        // Advance the cursor onto EOF, as it would be after `a` matched ID.
16586        parser.consume();
16587        assert_eq!(parser.la(1), TOKEN_EOF);
16588
16589        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
16590        // exactly what finish_rule(consumed_eof = false) records.
16591        let mut ctx = ParserRuleContext::new(0, 0);
16592        parser.set_context_stop(
16593            &mut ctx,
16594            parser.token_id_at(0).expect("ID token should be buffered"),
16595        );
16596        let tree = parser.rule_node(ctx);
16597
16598        let current_index = parser.input.index();
16599        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
16600        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
16601        // ...but the tree-aware helper follows the rule context stop (ID).
16602        assert_eq!(
16603            parser.after_action_stop_index_for_tree(tree, current_index),
16604            Some(0)
16605        );
16606    }
16607
16608    #[test]
16609    fn after_action_start_uses_rule_context_start_not_cursor() {
16610        // A rule that begins after leading hidden-channel tokens: the rule context
16611        // start (set by `enter_rule`) is the first visible token, not the raw cursor
16612        // that may still point at the hidden prefix. The @after start must follow
16613        // the context start so `$start`/`$text` excludes the hidden prefix.
16614        let mut parser = mini_parser(vec![
16615            TestToken::new(9)
16616                .with_text(" ")
16617                .with_channel(HIDDEN_CHANNEL),
16618            TestToken::new(9)
16619                .with_text(" ")
16620                .with_channel(HIDDEN_CHANNEL),
16621            TestToken::new(1).with_text("x"),
16622            TestToken::eof("parser-test", 3, 1, 3),
16623        ]);
16624
16625        let mut ctx = ParserRuleContext::new(0, 0);
16626        parser.set_context_start(
16627            &mut ctx,
16628            parser.token_id_at(2).expect("ID token should be buffered"),
16629        );
16630        let tree = parser.rule_node(ctx);
16631
16632        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
16633        // ...but the tree-aware helper follows the rule context start (index 2).
16634        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
16635
16636        // With no rule start recorded, it falls back to the provided index.
16637        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
16638        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
16639    }
16640
16641    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
16642        FastRecognizeOutcome {
16643            index,
16644            consumed_eof,
16645            diagnostics: DiagnosticSeqId::EMPTY,
16646            deferred_nodes: FastDeferredNodeId::EMPTY,
16647            nodes: NodeSeqId(marker),
16648        }
16649    }
16650
16651    #[test]
16652    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
16653        let mut outcomes = vec![
16654            clean_fast_outcome(4, false, 0),
16655            clean_fast_outcome(2, false, 1),
16656            clean_fast_outcome(4, false, 2),
16657            clean_fast_outcome(4, true, 3),
16658            clean_fast_outcome(2, false, 4),
16659        ];
16660        let mut scratch = FastOutcomeDedupScratch::default();
16661
16662        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16663
16664        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
16665        assert_eq!(
16666            outcomes
16667                .iter()
16668                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
16669                .collect::<Vec<_>>(),
16670            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
16671        );
16672        assert!(scratch.dense_words.is_empty());
16673        assert!(scratch.sparse_keys.is_empty());
16674    }
16675
16676    #[test]
16677    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
16678        let mut scratch = FastOutcomeDedupScratch::default();
16679        let mut outcomes = (100..109)
16680            .flat_map(|index| {
16681                [
16682                    clean_fast_outcome(
16683                        index,
16684                        false,
16685                        u32::try_from(index).expect("test index fits in u32"),
16686                    ),
16687                    clean_fast_outcome(index, false, u32::MAX),
16688                ]
16689            })
16690            .collect();
16691
16692        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16693
16694        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16695        assert_eq!(outcomes.len(), 9);
16696        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
16697        let dense_capacity = scratch.dense_words.capacity();
16698
16699        let mut reused = (1_000..1_009)
16700            .map(|index| {
16701                clean_fast_outcome(
16702                    index,
16703                    false,
16704                    u32::try_from(index).expect("test index fits in u32"),
16705                )
16706            })
16707            .collect();
16708        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16709
16710        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
16711        assert_eq!(reused.len(), 9);
16712        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
16713    }
16714
16715    #[test]
16716    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
16717        let mut scratch = FastOutcomeDedupScratch::default();
16718        let sparse_indexes = [
16719            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
16720        ];
16721        let mut outcomes = sparse_indexes
16722            .into_iter()
16723            .chain([400_000])
16724            .enumerate()
16725            .map(|(marker, index)| {
16726                clean_fast_outcome(
16727                    index,
16728                    false,
16729                    u32::try_from(marker).expect("test marker fits in u32"),
16730                )
16731            })
16732            .collect();
16733
16734        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16735
16736        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16737        assert_eq!(outcomes.len(), sparse_indexes.len());
16738        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
16739        let sparse_capacity = scratch.sparse_keys.capacity();
16740
16741        let mut reused = sparse_indexes
16742            .into_iter()
16743            .map(|index| {
16744                clean_fast_outcome(
16745                    index,
16746                    false,
16747                    u32::try_from(index).expect("test index fits in u32"),
16748                )
16749            })
16750            .collect();
16751        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
16752
16753        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16754        assert_eq!(reused.len(), sparse_indexes.len());
16755        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
16756    }
16757
16758    #[test]
16759    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
16760        let mut scratch = FastOutcomeDedupScratch::default();
16761        scratch
16762            .sparse_keys
16763            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
16764        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16765        let mut outcomes = (0..9)
16766            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
16767            .collect();
16768
16769        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
16770
16771        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
16772        assert!(scratch.sparse_keys.is_empty());
16773        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
16774    }
16775
16776    #[test]
16777    fn fast_outcome_selection_respects_sll_tie_order() {
16778        let mut arena = RecognitionArena::default();
16779        let first = FastRecognizeOutcome {
16780            index: 1,
16781            consumed_eof: false,
16782            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16783                line: 1,
16784                column: 0,
16785                message: "mismatched input 'x'".to_owned(),
16786            }]),
16787            deferred_nodes: FastDeferredNodeId::EMPTY,
16788            nodes: NodeSeqId::EMPTY,
16789        };
16790        let second = FastRecognizeOutcome {
16791            index: first.index,
16792            consumed_eof: first.consumed_eof,
16793            diagnostics: DiagnosticSeqId::EMPTY,
16794            deferred_nodes: FastDeferredNodeId::EMPTY,
16795            nodes: NodeSeqId::EMPTY,
16796        };
16797
16798        let selected = select_best_fast_outcome(
16799            [first, second].into_iter(),
16800            PredictionMode::Sll,
16801            None,
16802            |_| panic!("caller-follow token probe should not run"),
16803            &arena,
16804        )
16805        .expect("one outcome should be selected");
16806        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
16807        let eof_second = FastRecognizeOutcome {
16808            index: second.index,
16809            consumed_eof: true,
16810            diagnostics: DiagnosticSeqId::EMPTY,
16811            deferred_nodes: FastDeferredNodeId::EMPTY,
16812            nodes: NodeSeqId::EMPTY,
16813        };
16814        let selected = select_best_fast_outcome(
16815            [first, eof_second].into_iter(),
16816            PredictionMode::Sll,
16817            None,
16818            |_| panic!("caller-follow token probe should not run"),
16819            &arena,
16820        )
16821        .expect("one outcome should be selected");
16822        assert!(!selected.consumed_eof);
16823        let selected = select_best_fast_outcome(
16824            [first, second].into_iter(),
16825            PredictionMode::Ll,
16826            None,
16827            |_| panic!("caller-follow token probe should not run"),
16828            &arena,
16829        )
16830        .expect("one outcome should be selected");
16831        assert!(selected.diagnostics.is_empty());
16832    }
16833
16834    #[test]
16835    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
16836        let mut arena = RecognitionArena::default();
16837        let first = FastRecognizeOutcome {
16838            index: 3,
16839            consumed_eof: false,
16840            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16841                line: 1,
16842                column: 0,
16843                message: "mismatched input 'x' expecting 'a'".to_owned(),
16844            }]),
16845            deferred_nodes: FastDeferredNodeId::EMPTY,
16846            nodes: NodeSeqId::EMPTY,
16847        };
16848        let same_rank = FastRecognizeOutcome {
16849            index: first.index,
16850            consumed_eof: first.consumed_eof,
16851            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16852                line: 1,
16853                column: 0,
16854                message: "mismatched input 'x' expecting 'b'".to_owned(),
16855            }]),
16856            deferred_nodes: FastDeferredNodeId::EMPTY,
16857            nodes: NodeSeqId::EMPTY,
16858        };
16859        let better_rank = FastRecognizeOutcome {
16860            index: first.index,
16861            consumed_eof: first.consumed_eof,
16862            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
16863                line: 1,
16864                column: 0,
16865                message: "missing 'a' at 'x'".to_owned(),
16866            }]),
16867            deferred_nodes: FastDeferredNodeId::EMPTY,
16868            nodes: NodeSeqId::EMPTY,
16869        };
16870        let mut outcomes = vec![first, same_rank, better_rank];
16871
16872        dedupe_fast_outcomes(&mut outcomes, &arena);
16873
16874        assert_eq!(outcomes.len(), 2);
16875        assert_eq!(
16876            arena
16877                .diagnostics(outcomes[0].diagnostics)
16878                .next()
16879                .expect("first diagnostic")
16880                .message,
16881            "mismatched input 'x' expecting 'a'"
16882        );
16883        assert_eq!(
16884            arena
16885                .diagnostics(outcomes[1].diagnostics)
16886                .next()
16887                .expect("second diagnostic")
16888                .message,
16889            "missing 'a' at 'x'"
16890        );
16891    }
16892
16893    #[test]
16894    fn fast_outcome_selection_prefers_generated_caller_follow() {
16895        let arena = RecognitionArena::default();
16896        let earlier = FastRecognizeOutcome {
16897            index: 7,
16898            consumed_eof: false,
16899            diagnostics: DiagnosticSeqId::EMPTY,
16900            deferred_nodes: FastDeferredNodeId::EMPTY,
16901            nodes: NodeSeqId::EMPTY,
16902        };
16903        let later = FastRecognizeOutcome {
16904            index: 8,
16905            consumed_eof: false,
16906            diagnostics: DiagnosticSeqId::EMPTY,
16907            deferred_nodes: FastDeferredNodeId::EMPTY,
16908            nodes: NodeSeqId::EMPTY,
16909        };
16910        let mut follow = TokenBitSet::default();
16911        follow.insert(5);
16912
16913        let selected = select_best_fast_outcome(
16914            [later, earlier].into_iter(),
16915            PredictionMode::Ll,
16916            Some(&follow),
16917            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
16918            &arena,
16919        )
16920        .expect("one outcome should be selected");
16921        assert_eq!(selected.index, 7);
16922
16923        let selected = select_best_fast_outcome(
16924            [later, earlier].into_iter(),
16925            PredictionMode::Ll,
16926            Some(&follow),
16927            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
16928            &arena,
16929        )
16930        .expect("one outcome should be selected");
16931        assert_eq!(selected.index, 8);
16932
16933        let indented_next_statement = FastRecognizeOutcome {
16934            index: 9,
16935            consumed_eof: false,
16936            diagnostics: DiagnosticSeqId::EMPTY,
16937            deferred_nodes: FastDeferredNodeId::EMPTY,
16938            nodes: NodeSeqId::EMPTY,
16939        };
16940        let selected = select_best_fast_outcome(
16941            [indented_next_statement, earlier].into_iter(),
16942            PredictionMode::Ll,
16943            Some(&follow),
16944            |index| {
16945                let is_boundary = index == 7;
16946                let is_boundary_gap = matches!(index, 7 | 8);
16947                (
16948                    if index == 7 { 5 } else { TOKEN_EOF },
16949                    is_boundary,
16950                    is_boundary_gap,
16951                )
16952            },
16953            &arena,
16954        )
16955        .expect("one outcome should be selected");
16956        assert_eq!(selected.index, 7);
16957
16958        let continuation = FastRecognizeOutcome {
16959            index: 10,
16960            consumed_eof: false,
16961            diagnostics: DiagnosticSeqId::EMPTY,
16962            deferred_nodes: FastDeferredNodeId::EMPTY,
16963            nodes: NodeSeqId::EMPTY,
16964        };
16965        let selected = select_best_fast_outcome(
16966            [continuation, earlier].into_iter(),
16967            PredictionMode::Ll,
16968            Some(&follow),
16969            |index| {
16970                let is_boundary = matches!(index, 7 | 9);
16971                (
16972                    if index == 7 { 5 } else { TOKEN_EOF },
16973                    is_boundary,
16974                    is_boundary,
16975                )
16976            },
16977            &arena,
16978        )
16979        .expect("one outcome should be selected");
16980        assert_eq!(selected.index, 10);
16981
16982        let selected = select_best_fast_outcome(
16983            [earlier, later].into_iter(),
16984            PredictionMode::Sll,
16985            Some(&follow),
16986            |_| panic!("caller-follow token probe should not run in SLL mode"),
16987            &arena,
16988        )
16989        .expect("one outcome should be selected");
16990        assert_eq!(selected.index, 8);
16991    }
16992
16993    #[test]
16994    fn caller_follow_boundary_text_requires_separator_shape() {
16995        assert!(is_caller_follow_boundary_text(";"));
16996        assert!(is_caller_follow_boundary_text("\n"));
16997        assert!(is_caller_follow_boundary_text("\r\n  "));
16998        assert!(is_caller_follow_boundary_text(";\n"));
16999        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
17000        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
17001        assert!(!is_caller_follow_boundary_text("identifier"));
17002        assert!(is_caller_follow_boundary_gap_text(" \t "));
17003        assert!(is_caller_follow_boundary_gap_text("\n  "));
17004        assert!(is_caller_follow_boundary_gap_text(";\t"));
17005        assert!(!is_caller_follow_boundary_gap_text(
17006            "\"\"\"line1\nline2\"\"\""
17007        ));
17008        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
17009    }
17010
17011    #[test]
17012    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
17013        let mut parser = mini_parser(vec![
17014            TestToken::new(5).with_text("\n"),
17015            TestToken::new(6)
17016                .with_text("// comment\n")
17017                .with_channel(HIDDEN_CHANNEL),
17018            TestToken::new(1).with_text("x"),
17019            TestToken::eof("parser-test", 1, 2, 0),
17020        ]);
17021
17022        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17023        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
17024        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
17025    }
17026
17027    #[test]
17028    fn caller_follow_token_info_uses_stream_visible_channel() {
17029        let source = Source {
17030            tokens: vec![
17031                TestToken::new(5).with_text("\n").with_channel(2),
17032                TestToken::new(1).with_text("x").with_channel(2),
17033                TestToken::new(6)
17034                    .with_text("// comment\n")
17035                    .with_channel(HIDDEN_CHANNEL),
17036                TestToken::eof("parser-test", 1, 2, 0),
17037            ],
17038            index: 0,
17039        };
17040        let data = RecognizerData::new(
17041            "Mini.g4",
17042            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17043        );
17044        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
17045
17046        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
17047        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
17048        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
17049    }
17050
17051    #[test]
17052    fn reset_per_parse_caches_clears_state_expected_token_cache() {
17053        let atn = token_then_eof_atn();
17054        let mut parser = mini_parser(Vec::new());
17055
17056        let _ = parser.cached_state_expected_token_set(&atn, 0);
17057        assert!(!parser.state_expected_token_cache.is_empty());
17058
17059        parser.reset_per_parse_caches();
17060        assert!(parser.state_expected_token_cache.is_empty());
17061    }
17062
17063    #[test]
17064    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
17065        let cyclic = epsilon_cycle_atn();
17066        let acyclic = token_then_eof_atn();
17067        let mut parser = mini_parser(Vec::new());
17068
17069        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17070        assert_eq!(
17071            parser.empty_cycle_cache_atn,
17072            Some(SharedAtnCacheKey::for_atn(&cyclic))
17073        );
17074        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17075
17076        parser.reset_per_parse_caches();
17077        assert_eq!(parser.empty_cycle_cache[1], Some(true));
17078        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
17079
17080        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
17081        assert_eq!(
17082            parser.empty_cycle_cache_atn,
17083            Some(SharedAtnCacheKey::for_atn(&acyclic))
17084        );
17085        assert_eq!(parser.empty_cycle_cache[1], Some(false));
17086    }
17087
17088    #[test]
17089    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
17090        let source = Source {
17091            tokens: vec![
17092                TestToken::new(1).with_text("x"),
17093                TestToken::eof("parser-test", 1, 1, 1),
17094            ],
17095            index: 0,
17096        };
17097        let data = RecognizerData::new(
17098            "Mini.g4",
17099            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17100        );
17101        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17102        let expected = ExpectedTokens {
17103            index: Some(0),
17104            symbols: BTreeSet::new(),
17105            no_viable: None,
17106        };
17107
17108        let (_, message) = parser.expected_error_message(0, 0, &expected);
17109
17110        assert_eq!(message, "mismatched input 'x'");
17111    }
17112
17113    #[test]
17114    fn eof_rule_stop_index_points_at_eof_token() {
17115        let source = Source {
17116            tokens: vec![
17117                TestToken::new(1).with_text("x"),
17118                TestToken::eof("parser-test", 1, 1, 1),
17119            ],
17120            index: 0,
17121        };
17122        let data = RecognizerData::new(
17123            "Mini.g4",
17124            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17125        );
17126        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17127
17128        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
17129        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
17130    }
17131
17132    #[test]
17133    fn generated_parser_action_uses_current_rule_stop_boundary() {
17134        let mut parser = mini_parser(vec![
17135            TestToken::new(1).with_text("x"),
17136            TestToken::eof("parser-test", 1, 1, 1),
17137        ]);
17138
17139        parser.match_token(1).expect("token should match");
17140        let action = parser.parser_action_at_current(7, 0, 0, false);
17141        assert_eq!(action.source_state(), 7);
17142        assert_eq!(action.rule_index(), 0);
17143        assert_eq!(action.start_index(), 0);
17144        assert_eq!(action.stop_index(), Some(0));
17145
17146        parser.match_eof().expect("EOF should match");
17147        let action = parser.parser_action_at_current(8, 0, 0, true);
17148        assert_eq!(action.stop_index(), Some(1));
17149    }
17150
17151    #[test]
17152    fn folds_left_recursive_boundary_into_rule_node() {
17153        let mut arena = RecognitionArena::default();
17154        let first = arena.push_node(ArenaRecognizedNode::Token {
17155            token: TokenId::try_from(0).expect("test token ID"),
17156        });
17157        let boundary =
17158            arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary { rule_index: 1 });
17159        let second = arena.push_node(ArenaRecognizedNode::Token {
17160            token: TokenId::try_from(1).expect("test token ID"),
17161        });
17162        let mut nodes = NodeSeqId::EMPTY;
17163        for node in [first, boundary, second].into_iter().rev() {
17164            nodes = arena.prepend(nodes, node);
17165        }
17166
17167        let folded = arena.fold_left_recursive_boundaries(nodes);
17168        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
17169
17170        assert_eq!(folded_nodes.len(), 2);
17171        let ArenaRecognizedNode::Rule {
17172            rule_index,
17173            invoking_state,
17174            start_index,
17175            stop_index,
17176            children,
17177            ..
17178        } = arena.node(folded_nodes[0])
17179        else {
17180            panic!("first folded node should be a rule");
17181        };
17182        assert_eq!(rule_index, 1);
17183        assert_eq!(invoking_state, -1);
17184        assert_eq!(start_index, 0);
17185        assert_eq!(stop_index, Some(0));
17186        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
17187        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
17188
17189        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
17190        assert_eq!(
17191            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17192            (4, 3, 1)
17193        );
17194        assert_eq!(
17195            (stats.total_links, stats.live_links, stats.dead_links),
17196            (9, 3, 6)
17197        );
17198    }
17199
17200    #[test]
17201    fn recognition_arena_reports_live_dead_and_retained_capacity() {
17202        let mut arena = RecognitionArena::default();
17203        let token = arena.push_node(ArenaRecognizedNode::Token {
17204            token: TokenId::try_from(0).expect("test token ID"),
17205        });
17206        let extra = arena.push_extra(RecognitionExtra::MissingToken {
17207            token_type: 2,
17208            at_index: 1,
17209            text: "<missing X>".to_owned(),
17210        });
17211        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
17212        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
17213            token: TokenId::try_from(1).expect("test token ID"),
17214        });
17215        let mut live = NodeSeqId::EMPTY;
17216        live = arena.prepend(live, missing);
17217        live = arena.prepend(live, token);
17218        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
17219        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17220            line: 1,
17221            column: 0,
17222            message: "missing X".to_owned(),
17223        }]);
17224        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17225            line: 1,
17226            column: 1,
17227            message: "discarded".to_owned(),
17228        }]);
17229        let deferred_children = arena.deferred_fragment(live);
17230        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
17231            rule_index: 0,
17232            invoking_state: -1,
17233            start_index: 0,
17234            stop_index: Some(1),
17235            deferred_children,
17236            children: NodeSeqId::EMPTY,
17237        });
17238
17239        let stats = arena.stats(live, live_diagnostics);
17240
17241        assert_eq!(
17242            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17243            (3, 2, 1)
17244        );
17245        assert_eq!(
17246            (stats.total_links, stats.live_links, stats.dead_links),
17247            (5, 3, 2)
17248        );
17249        assert_eq!(
17250            (stats.total_extras, stats.live_extras, stats.dead_extras),
17251            (3, 2, 1)
17252        );
17253        assert!(size_of::<SeqLink>() <= 8);
17254        assert!(size_of::<DiagnosticLink>() <= 8);
17255        assert!(size_of::<FastDeferredNode>() <= 12);
17256        assert!(size_of::<FastDeferredRule>() <= 28);
17257        assert!(size_of::<FastRecognizeOutcome>() <= 24);
17258        let capacities = (
17259            stats.node_capacity,
17260            stats.link_capacity,
17261            stats.extra_capacity,
17262        );
17263        let deferred_capacities = (
17264            arena.deferred_nodes.capacity(),
17265            arena.deferred_rules.capacity(),
17266        );
17267
17268        arena.reset();
17269        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
17270        assert_eq!(
17271            (reset.total_nodes, reset.total_links, reset.total_extras),
17272            (0, 0, 0)
17273        );
17274        assert_eq!(
17275            (
17276                reset.node_capacity,
17277                reset.link_capacity,
17278                reset.extra_capacity,
17279            ),
17280            capacities
17281        );
17282        assert!(arena.deferred_nodes.is_empty());
17283        assert!(arena.deferred_rules.is_empty());
17284        assert_eq!(
17285            (
17286                arena.deferred_nodes.capacity(),
17287                arena.deferred_rules.capacity(),
17288            ),
17289            deferred_capacities
17290        );
17291    }
17292
17293    #[test]
17294    fn parser_computes_recognition_arena_stats_on_demand() {
17295        let mut parser = mini_parser(Vec::new());
17296        let live = parser
17297            .recognition_arena
17298            .push_node(ArenaRecognizedNode::Token {
17299                token: TokenId::try_from(0).expect("test token ID"),
17300            });
17301        let discarded = parser
17302            .recognition_arena
17303            .push_node(ArenaRecognizedNode::ErrorToken {
17304                token: TokenId::try_from(1).expect("test token ID"),
17305            });
17306        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
17307        let _discarded_root = parser
17308            .recognition_arena
17309            .prepend(NodeSeqId::EMPTY, discarded);
17310        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
17311
17312        let stats = parser.recognition_arena_stats();
17313
17314        assert_eq!(
17315            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
17316            (2, 1, 1)
17317        );
17318        assert_eq!(
17319            (stats.total_links, stats.live_links, stats.dead_links),
17320            (2, 1, 1)
17321        );
17322    }
17323
17324    #[test]
17325    fn recognition_arena_drops_capacity_above_retention_limit() {
17326        let mut storage = Vec::<u8>::with_capacity(4);
17327        storage.extend([1, 2, 3]);
17328
17329        reset_arena_vec(&mut storage, 3);
17330
17331        assert!(storage.is_empty());
17332        assert_eq!(storage.capacity(), 0);
17333    }
17334
17335    #[test]
17336    fn recognition_arena_concatenates_diagnostics_in_source_order() {
17337        let mut arena = RecognitionArena::default();
17338        let prefix = arena.diagnostic_sequence([
17339            ParserDiagnostic {
17340                line: 1,
17341                column: 0,
17342                message: "first".to_owned(),
17343            },
17344            ParserDiagnostic {
17345                line: 1,
17346                column: 1,
17347                message: "second".to_owned(),
17348            },
17349        ]);
17350        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
17351            line: 1,
17352            column: 2,
17353            message: "third".to_owned(),
17354        }]);
17355        let extras_before = arena.extras.len();
17356
17357        let combined = arena.concat_diagnostics(prefix, suffix);
17358        let messages = arena
17359            .diagnostics(combined)
17360            .map(|diagnostic| diagnostic.message.as_str())
17361            .collect::<Vec<_>>();
17362
17363        assert_eq!(messages, ["first", "second", "third"]);
17364        assert_eq!(arena.extras.len(), extras_before);
17365    }
17366
17367    #[test]
17368    fn outcome_ties_keep_later_non_recursive_alternative() {
17369        let arena = RecognitionArena::default();
17370        let first = RecognizeOutcome {
17371            index: 1,
17372            consumed_eof: false,
17373            alt_number: 0,
17374            member_values: BTreeMap::new(),
17375            return_values: BTreeMap::new(),
17376            diagnostics: DiagnosticSeqId::EMPTY,
17377            decisions: Vec::new(),
17378            actions: vec![ParserAction::new(1, 0, 0, None)],
17379            nodes: NodeSeqId::EMPTY,
17380        };
17381        let second = RecognizeOutcome {
17382            actions: vec![ParserAction::new(2, 0, 0, None)],
17383            ..first.clone()
17384        };
17385
17386        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17387            .expect("one outcome should be selected");
17388        assert_eq!(selected.actions[0].source_state(), 2);
17389    }
17390
17391    #[test]
17392    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
17393        let arena = RecognitionArena::default();
17394        let first = RecognizeOutcome {
17395            index: 1,
17396            consumed_eof: false,
17397            alt_number: 0,
17398            member_values: BTreeMap::new(),
17399            return_values: BTreeMap::new(),
17400            diagnostics: DiagnosticSeqId::EMPTY,
17401            decisions: Vec::new(),
17402            actions: vec![ParserAction::new(1, 0, 0, None)],
17403            nodes: NodeSeqId::EMPTY,
17404        };
17405        let second = RecognizeOutcome {
17406            actions: vec![
17407                ParserAction::new(2, 0, 0, None),
17408                ParserAction::new(3, 0, 0, None),
17409            ],
17410            ..first.clone()
17411        };
17412
17413        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
17414            .expect("one outcome should be selected");
17415        assert_eq!(selected.actions.len(), 2);
17416    }
17417
17418    #[test]
17419    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
17420        let arena = RecognitionArena::default();
17421        let first = RecognizeOutcome {
17422            index: 7,
17423            consumed_eof: false,
17424            alt_number: 0,
17425            member_values: BTreeMap::new(),
17426            return_values: BTreeMap::new(),
17427            diagnostics: DiagnosticSeqId::EMPTY,
17428            decisions: vec![1, 0],
17429            actions: vec![
17430                ParserAction::new(23, 2, 2, Some(4)),
17431                ParserAction::new(23, 2, 0, Some(6)),
17432            ],
17433            nodes: NodeSeqId::EMPTY,
17434        };
17435        let second = RecognizeOutcome {
17436            decisions: vec![0, 1],
17437            actions: vec![
17438                ParserAction::new(23, 2, 2, Some(6)),
17439                ParserAction::new(23, 2, 0, Some(6)),
17440            ],
17441            ..first.clone()
17442        };
17443
17444        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17445            .expect("one outcome should be selected");
17446        assert_eq!(selected.actions[0].stop_index(), Some(6));
17447    }
17448
17449    #[test]
17450    fn outcome_ties_keep_first_recursive_tree_shape() {
17451        let mut arena = RecognitionArena::default();
17452        let token = arena.push_node(ArenaRecognizedNode::Token {
17453            token: TokenId::try_from(0).expect("test token ID"),
17454        });
17455        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
17456        let inner = arena.push_node(ArenaRecognizedNode::Rule {
17457            rule_index: 1,
17458            invoking_state: -1,
17459            alt_number: 0,
17460            start_index: 0,
17461            stop_index: Some(0),
17462            return_values: None,
17463            children: token_children,
17464        });
17465        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
17466        let outer = arena.push_node(ArenaRecognizedNode::Rule {
17467            rule_index: 1,
17468            invoking_state: -1,
17469            alt_number: 0,
17470            start_index: 0,
17471            stop_index: Some(0),
17472            return_values: None,
17473            children: inner_children,
17474        });
17475        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
17476        let first = RecognizeOutcome {
17477            index: 1,
17478            consumed_eof: false,
17479            alt_number: 0,
17480            member_values: BTreeMap::new(),
17481            return_values: BTreeMap::new(),
17482            diagnostics: DiagnosticSeqId::EMPTY,
17483            decisions: Vec::new(),
17484            actions: vec![ParserAction::new(1, 0, 0, None)],
17485            nodes: recursive_nodes,
17486        };
17487        let second = RecognizeOutcome {
17488            index: 1,
17489            consumed_eof: false,
17490            alt_number: 0,
17491            member_values: BTreeMap::new(),
17492            return_values: BTreeMap::new(),
17493            diagnostics: DiagnosticSeqId::EMPTY,
17494            decisions: Vec::new(),
17495            actions: vec![ParserAction::new(2, 0, 0, None)],
17496            nodes: recursive_nodes,
17497        };
17498
17499        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
17500            .expect("one outcome should be selected");
17501        assert_eq!(selected.actions[0].source_state(), 1);
17502    }
17503
17504    #[test]
17505    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
17506        let mut arena = RecognitionArena::default();
17507        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
17508            line: 1,
17509            column: 3,
17510            message: "missing 'Y' at '<EOF>'".to_owned(),
17511        }]);
17512        let first_alt = RecognizeOutcome {
17513            index: 2,
17514            consumed_eof: true,
17515            alt_number: 0,
17516            member_values: BTreeMap::new(),
17517            return_values: BTreeMap::new(),
17518            diagnostics: recovered_diagnostics,
17519            decisions: vec![0],
17520            actions: vec![ParserAction::new(1, 0, 0, None)],
17521            nodes: NodeSeqId::EMPTY,
17522        };
17523        let second_alt = RecognizeOutcome {
17524            diagnostics: DiagnosticSeqId::EMPTY,
17525            decisions: vec![1],
17526            actions: vec![ParserAction::new(2, 0, 0, None)],
17527            ..first_alt.clone()
17528        };
17529
17530        let selected = select_best_outcome(
17531            [second_alt, first_alt].into_iter(),
17532            PredictionMode::Sll,
17533            &arena,
17534        )
17535        .expect("one outcome should be selected");
17536        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
17537        assert_eq!(selected.decisions, [0]);
17538    }
17539}