Skip to main content

antlr4_runtime/
parser.rs

1// `HashMap`/`HashSet` here are used as parser-internal caches keyed on
2// stable ATN coordinates (state numbers, token indices). They're never
3// iterated externally, so the project's `disallowed_types` lint (which
4// guards against non-deterministic iteration order leaking out) does not
5// apply to these uses.
6use std::cell::RefCell;
7use std::cmp::Ordering;
8#[allow(clippy::disallowed_types)]
9use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
10use std::hash::{BuildHasherDefault, Hash, Hasher};
11use std::rc::Rc;
12
13/// Rotate constant copied from rustc-hash / `FxHash`. The default
14/// `RandomState` hasher seeds itself from the OS RNG and runs `SipHash` on
15/// every key, which dominates `recognize_state_fast`'s memo lookups;
16/// `FxHasher` is a streaming integer hasher with near-zero per-call overhead
17/// and matches the access pattern of small integer keys that the parser memo
18/// uses.
19#[derive(Clone, Copy, Default)]
20struct FxHasher {
21    hash: u64,
22}
23
24const FX_ROT: u32 = 5;
25const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
26
27impl Hasher for FxHasher {
28    /// Folds bytes 8 at a time so a `write(&[u8; 8])` call hashes to the same
29    /// state as a `write_u64` of the same little-endian bits. The `Hash` impls
30    /// for `String`, `[u8; N]`, and slice-like types reach the hasher through
31    /// `write`; matching the typed-method behaviour avoids the silent
32    /// divergence flagged in PR #5 review (Greptile P2). Tail bytes that do
33    /// not form a full word are mixed one at a time with the same constants,
34    /// keeping behaviour deterministic regardless of the slice length.
35    #[inline]
36    fn write(&mut self, mut bytes: &[u8]) {
37        while bytes.len() >= 8 {
38            let (head, rest) = bytes.split_at(8);
39            let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
40            self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
41            bytes = rest;
42        }
43        for byte in bytes {
44            self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(*byte)).wrapping_mul(FX_SEED);
45        }
46    }
47    #[inline]
48    fn write_u64(&mut self, value: u64) {
49        self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
50    }
51    #[inline]
52    fn write_usize(&mut self, value: usize) {
53        self.write_u64(value as u64);
54    }
55    #[inline]
56    fn write_u32(&mut self, value: u32) {
57        self.write_u64(u64::from(value));
58    }
59    #[inline]
60    fn write_i32(&mut self, value: i32) {
61        self.write_u64(u64::from(i32::cast_unsigned(value)));
62    }
63    #[inline]
64    fn finish(&self) -> u64 {
65        self.hash
66    }
67}
68
69type FxBuildHasher = BuildHasherDefault<FxHasher>;
70#[allow(clippy::disallowed_types)]
71type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
72#[allow(clippy::disallowed_types)]
73type FxHashSet<K> = HashSet<K, FxBuildHasher>;
74
75use crate::atn::AtnStateKind;
76use crate::atn::parser::{
77    ParserAtnPrediction, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
78};
79use crate::atn::parser_atn::{
80    ParserAtn as Atn, ParserAtnState as AtnState, ParserIntervalSet, ParserTransition,
81    ParserTransitionData as Transition, ParserTransitionKind,
82};
83#[cfg(test)]
84use crate::atn::parser_atn::{ParserAtnBuilder, ParserTransitionSpec};
85use crate::char_stream::CharStream;
86use crate::errors::AntlrError;
87use crate::int_stream::IntStream;
88use crate::lexer::{LexerCustomAction, LexerLifecycleCtx, LexerSemCtx};
89use crate::recognizer::{Recognizer, RecognizerData};
90use crate::semir::{self, AStmt, ArithOp, CmpOp, ExprId, HookId, PExpr, SemIr, StmtId};
91use crate::token::{
92    TOKEN_EOF, Token, TokenId, TokenSource, TokenSourceError, TokenSpec, TokenStore, TokenView,
93};
94use crate::token_stream::CommonTokenStream;
95use crate::tree::{
96    Node, NodeId, ParseTreeCheckpoint, ParseTreeStorage, ParsedFile, ParserRuleContext,
97};
98use crate::vocabulary::Vocabulary;
99
100type ParseTree = NodeId;
101
102/// Upper bound for the recursive metadata recognizer before it treats a path as
103/// non-viable. Long expression-regression descriptors legitimately walk tens
104/// of thousands of ATN edges.
105const RECOGNITION_DEPTH_LIMIT: usize = 32_768;
106/// Preserve the recursive hot path while checking native stack capacity often
107/// enough that one unchecked group cannot cross the protected red zone.
108const FAST_RECOGNIZE_STACK_CHECK_INTERVAL: usize = 8;
109const FAST_RECOGNIZE_RED_ZONE: usize = 1024 * 1024;
110const FAST_RECOGNIZE_STACK_SIZE: usize = 4 * 1024 * 1024;
111/// Generated recursive-descent rule methods map grammar-rule nesting onto
112/// native call depth. Their `_dispatch` boundary samples remaining stack
113/// capacity once per this many rule-context frames, so between two samples at
114/// most this many rule bodies of native growth can occur — far below the
115/// red zone.
116const GENERATED_RULE_STACK_CHECK_INTERVAL: usize = 8;
117/// Whole-rule direct adaptive execution is allowed to give up and fall back to
118/// the existing recognizer. Keep the guard at the same order of magnitude as
119/// speculative recognition so malformed cyclic ATNs cannot spin forever.
120const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
121
122/// Runs a generated rule body after ensuring native stack capacity, growing
123/// onto a segmented stack when remaining capacity enters the red zone.
124///
125/// Generated `parse_generated_rule_*_dispatch` methods call this when
126/// [`BaseParser::generated_rule_stack_check_due`] fires so deeply nested input
127/// parses (or reports a syntax error) instead of aborting the process.
128pub fn grow_generated_rule_stack<R>(body: impl FnOnce() -> R) -> R {
129    stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, body)
130}
131
132/// Receives committed rule enter/exit events during recognition, matching
133/// ANTLR's `addParseListener` contract ([`Parser::add_parse_listener`],
134/// also inherent on [`BaseParser`] and generated parsers).
135///
136/// Events fire on the generated recursive-descent path as rules are entered
137/// and exited, with left-recursive operator loops following upstream's
138/// timing exactly: each loop pass first exits the outgoing iteration
139/// (`recRuleSetPrevCtx`) and then enters the new expansion
140/// (`pushNewRecursionContext` firing `triggerEnterRuleEvent`), so live
141/// listener depth never accumulates across a flat operator chain —
142/// `a + a + … + a` peaks at depth 2 like every ANTLR target. On expansion
143/// events, [`EnterRuleEvent::current`] anchors at the operator-side
144/// lookahead (the token the expansion starts at), whereas Java's
145/// `ctx.start` reaches back to the whole expression's first token — anchor
146/// diagnostics accordingly. Enter events fire in registration order and
147/// exit events in reverse registration order, matching upstream. Enter/exit
148/// calls balance on every completed path, including error recovery and
149/// aborts inside operator loops — with one exception shared with Java: an
150/// ordinary rule's enter that returns `Err` receives no matching exit
151/// (upstream calls `enterRule` outside the generated `try`/`finally`, so a
152/// throwing listener skips `exitRule` the same way). Listener state shared
153/// across parses via `Arc` should be reset after an abort (the unmatched
154/// ordinary-rule enter leaves counters one high).
155///
156/// Divergence from Java to know about: upstream generated rule methods run
157/// only on the committed parse, while this runtime may re-enter a rule while
158/// recovering from a syntax error — such retries deliver additional balanced
159/// enter/exit pairs. Depth counters and resource bounds (the primary use
160/// case) are unaffected; exact once-per-node collectors should prefer the
161/// post-parse tree walker.
162///
163/// `enter_every_rule` is fallible: returning `Err` aborts the parse with
164/// that error. The abort is sticky through rule-level recovery — the parse
165/// fails even when recovery could have produced a tree, mirroring how a
166/// thrown exception escapes ANTLR's `triggerEnterRuleEvent`. Rules the
167/// generator emitted no body for (interpreter-only fallback) do not fire
168/// events; when any parse listener is registered, generated dispatch routes
169/// ATN-preferred rules through their generated bodies so real grammars
170/// observe every rule.
171///
172/// Cost: with no listener registered, dispatch pays one emptiness check per
173/// rule boundary (benchmarked at baseline). With one registered, dispatch
174/// itself is a few percent; on grammars where the generator classified rules
175/// ATN-preferred, the dominant cost is the routing override above — the same
176/// one [`Parser::set_max_rule_depth`] takes — which trades that fast path
177/// for observability. Grammars without ATN-preferred rules (most small DSLs)
178/// pay only the dispatch.
179pub trait ParseListener: Send {
180    /// Called when a generated rule is entered, before its body runs, and
181    /// once per left-recursive operator expansion.
182    ///
183    /// Returning `Err` aborts the parse with the given error.
184    fn enter_every_rule(&mut self, event: &EnterRuleEvent<'_>) -> Result<(), AntlrError>;
185
186    /// Called when a generated rule exits, after its body (and any rule-level
187    /// error recovery) finished, and once per left-recursive operator
188    /// expansion as the rule unrolls.
189    fn exit_every_rule(&mut self, rule_index: usize) {
190        let _ = rule_index;
191    }
192}
193
194/// Boxed listeners forward to their inner implementation, so the boxes
195/// returned by [`Parser::remove_parse_listeners`] can be re-registered
196/// through [`Parser::add_parse_listener`] unchanged.
197impl<T: ParseListener + ?Sized> ParseListener for Box<T> {
198    fn enter_every_rule(&mut self, event: &EnterRuleEvent<'_>) -> Result<(), AntlrError> {
199        (**self).enter_every_rule(event)
200    }
201
202    fn exit_every_rule(&mut self, rule_index: usize) {
203        (**self).exit_every_rule(rule_index);
204    }
205}
206
207/// A rule-entry event delivered to [`ParseListener::enter_every_rule`].
208///
209/// Non-exhaustive so future fields (alt number, invoking state, a context
210/// handle) extend the event without breaking implementors.
211#[derive(Debug)]
212#[non_exhaustive]
213pub struct EnterRuleEvent<'a> {
214    /// Index of the rule being entered (compare against the generated
215    /// `RULE_*` constants).
216    pub rule_index: usize,
217    /// The lookahead token the rule starts at — its line/column/offsets
218    /// anchor listener diagnostics — or `None` at end of input.
219    pub current: Option<TokenView<'a>>,
220}
221
222struct ParseListenerSlot(Box<dyn ParseListener>);
223
224impl std::fmt::Debug for ParseListenerSlot {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        f.write_str("ParseListener")
227    }
228}
229/// Probe window for deciding whether clean-pass memo entries are reusable
230/// enough to keep caching. High-cardinality parses mostly produce one-shot
231/// entries; compact ambiguous loops repeatedly hit the same keys.
232const CLEAN_MEMO_PROBE_LIMIT: usize = 4096;
233const CLEAN_MEMO_REPEAT_LIMIT: usize = 8;
234/// Sparse parses periodically reopen the bounded probe so a repeat-heavy
235/// region that starts later in the token stream can promote memoization.
236const CLEAN_MEMO_REPROBE_INTERVAL: usize = 262_144;
237const FAST_RECOGNIZE_VISITING_CAPACITY: usize = 256;
238const FAST_RECOGNIZE_MIN_MEMO_CAPACITY: usize = 256;
239const FAST_RECOGNIZE_MAX_MEMO_CAPACITY: usize = 524_288;
240const FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY: usize = 65_536;
241
242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
243enum CleanMemoMode {
244    Probe,
245    Promote,
246    Sparse,
247}
248
249fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
250    intervals
251        .iter()
252        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
253}
254
255fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
256    let mut symbols = BTreeSet::new();
257    for (start, stop) in intervals {
258        symbols.extend(*start..=*stop);
259    }
260    symbols
261}
262
263fn interval_complement_symbols(
264    intervals: &[(i32, i32)],
265    min_vocabulary: i32,
266    max_vocabulary: i32,
267) -> BTreeSet<i32> {
268    (min_vocabulary..=max_vocabulary)
269        .filter(|symbol| !interval_set_contains(intervals, *symbol))
270        .collect()
271}
272
273#[cfg(feature = "perf-counters")]
274mod perf_counters {
275    use std::cell::Cell;
276    thread_local! {
277        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
278        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
279        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
280        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
281        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
282        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
283        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
284        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
285        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
286        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
287        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
288        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
289        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
290    }
291    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
292        c.with(|v| v.set(v.get() + n));
293    }
294    thread_local! {
295        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
296        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
297        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
298        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
299        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
300        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
301        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
302        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
303        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
304        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
305        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
306    }
307    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
308        [
309            ("rfs_calls", RFS_CALLS.with(Cell::get)),
310            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
311            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
312            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
313            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
314            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
315            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
316            (
317                "outcome_dedupe_inputs",
318                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
319            ),
320            (
321                "outcome_dedupe_removed",
322                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
323            ),
324            (
325                "outcome_dedupe_inline",
326                OUTCOME_DEDUPE_INLINE.with(Cell::get),
327            ),
328            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
329            (
330                "outcome_dedupe_sparse",
331                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
332            ),
333            (
334                "outcome_dedupe_dense_words",
335                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
336            ),
337            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
338            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
339            (
340                "atom_range_transitions",
341                ATOM_RANGE_TRANSITIONS.with(Cell::get),
342            ),
343            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
344            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
345            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
346            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
347            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
348            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
349            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
350            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
351        ]
352    }
353    pub fn reset() {
354        RFS_CALLS.with(|c| c.set(0));
355        RFS_MEMO_HITS.with(|c| c.set(0));
356        RFS_MEMO_MISSES.with(|c| c.set(0));
357        RFS_VISITING_CYCLE.with(|c| c.set(0));
358        MEMO_INSERTED.with(|c| c.set(0));
359        OUTCOMES_PUSHED.with(|c| c.set(0));
360        OUTCOMES_CLONED.with(|c| c.set(0));
361        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
362        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
363        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
364        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
365        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
366        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
367        EPSILON_TRANSITIONS.with(|c| c.set(0));
368        RULE_TRANSITIONS.with(|c| c.set(0));
369        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
370        SINGLE_TRANS_BODY.with(|c| c.set(0));
371        MULTI_TRANS_BODY.with(|c| c.set(0));
372        SINGLE_TRANS_RULE.with(|c| c.set(0));
373        SINGLE_TRANS_ATOM.with(|c| c.set(0));
374        SINGLE_TRANS_OTHER.with(|c| c.set(0));
375        OUTCOMES_RETURN_0.with(|c| c.set(0));
376        OUTCOMES_RETURN_1.with(|c| c.set(0));
377        OUTCOMES_RETURN_N.with(|c| c.set(0));
378    }
379    pub fn dump() {
380        for (name, value) in snapshot() {
381            #[allow(clippy::print_stderr)]
382            {
383                eprintln!("perf {name}={value}");
384            }
385        }
386    }
387}
388
389#[cfg(feature = "perf-counters")]
390pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
391/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
392/// fast recognizer has probed far enough that per-token stream sync dominates.
393/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
394/// switching long inputs to the cheaper filled-stream path before large fanout.
395const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
396/// Parser semantic action reached while recognizing one ATN path.
397///
398/// Generated parsers use `source_state` to dispatch back to the grammar action
399/// rendered for that ATN action transition. The token interval is the current
400/// rule's input span at the action site, which covers common target templates
401/// such as `$text`. Rule-init actions do not have an ATN action source state,
402/// so they are marked separately and may carry an ATN state for expected-token
403/// rendering.
404#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
405pub struct ParserAction {
406    source_state: usize,
407    rule_index: usize,
408    start_index: usize,
409    stop_index: Option<usize>,
410    rule_init: bool,
411    expected_state: Option<usize>,
412}
413
414impl ParserAction {
415    /// Creates an action event for a recognized parser path.
416    pub const fn new(
417        source_state: usize,
418        rule_index: usize,
419        start_index: usize,
420        stop_index: Option<usize>,
421    ) -> Self {
422        Self {
423            source_state,
424            rule_index,
425            start_index,
426            stop_index,
427            rule_init: false,
428            expected_state: None,
429        }
430    }
431
432    /// Creates an action event for a rule-level `@init` action.
433    pub const fn new_rule_init(
434        rule_index: usize,
435        start_index: usize,
436        expected_state: Option<usize>,
437    ) -> Self {
438        Self {
439            source_state: usize::MAX,
440            rule_index,
441            start_index,
442            stop_index: None,
443            rule_init: true,
444            expected_state,
445        }
446    }
447
448    /// ATN state that owns the semantic-action transition.
449    pub const fn source_state(&self) -> usize {
450        self.source_state
451    }
452
453    /// Grammar rule index recorded by the serialized ATN action transition.
454    pub const fn rule_index(&self) -> usize {
455        self.rule_index
456    }
457
458    /// Token-stream index where the active rule began.
459    pub const fn start_index(&self) -> usize {
460        self.start_index
461    }
462
463    /// Last token-stream index consumed before the action was reached.
464    pub const fn stop_index(&self) -> Option<usize> {
465        self.stop_index
466    }
467
468    /// Reports whether this event represents a rule-level `@init` action.
469    pub const fn is_rule_init(&self) -> bool {
470        self.rule_init
471    }
472
473    /// ATN state used to compute expected-token display for this action.
474    pub const fn expected_state(&self) -> Option<usize> {
475        self.expected_state
476    }
477}
478
479/// Runtime view passed to parser semantic hooks.
480///
481/// The context is intentionally read-only with respect to parser structure:
482/// predicates may run speculatively during prediction, and hooks can be called
483/// more than once for paths that are later abandoned. Lookahead methods may
484/// buffer tokens from the underlying token source, matching normal parser
485/// prediction behavior.
486pub struct ParserSemCtx<'a, S>
487where
488    S: TokenSource,
489{
490    input: &'a mut CommonTokenStream<S>,
491    tree_storage: &'a ParseTreeStorage,
492    rule_index: usize,
493    coordinate_index: usize,
494    rule_name: Option<String>,
495    context: Option<&'a ParserRuleContext>,
496    tree: Option<ParseTree>,
497    local_int_arg: Option<(usize, i64)>,
498    member_values: &'a BTreeMap<usize, i64>,
499    action: Option<ParserAction>,
500}
501
502impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
503where
504    S: TokenSource,
505{
506    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
507        f.debug_struct("ParserSemCtx")
508            .field("rule_index", &self.rule_index)
509            .field("coordinate_index", &self.coordinate_index)
510            .field("rule_name", &self.rule_name)
511            .field("context", &self.context)
512            .field("tree", &self.tree)
513            .field("local_int_arg", &self.local_int_arg)
514            .field("member_values", &self.member_values)
515            .field("action", &self.action)
516            .finish_non_exhaustive()
517    }
518}
519
520impl<'a, S> ParserSemCtx<'a, S>
521where
522    S: TokenSource,
523{
524    /// Rule index that owns the predicate/action coordinate.
525    #[must_use]
526    pub const fn rule_index(&self) -> usize {
527        self.rule_index
528    }
529
530    /// Rule name that owns the coordinate, when recognizer metadata has it.
531    #[must_use]
532    pub fn rule_name(&self) -> Option<&str> {
533        self.rule_name.as_deref()
534    }
535
536    /// Predicate/action index inside the owning rule. Parser actions keyed only
537    /// by ATN source state report `usize::MAX` here; use [`Self::action`] for
538    /// the stable action event.
539    #[must_use]
540    pub const fn coordinate_index(&self) -> usize {
541        self.coordinate_index
542    }
543
544    /// Current token-stream index.
545    #[must_use]
546    pub fn input_index(&self) -> usize {
547        self.input.index()
548    }
549
550    /// Token type at one-based lookahead/lookbehind offset.
551    pub fn la(&mut self, offset: isize) -> i32 {
552        self.input.la(offset)
553    }
554
555    /// Token at one-based lookahead/lookbehind offset.
556    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
557        self.input.lt(offset)
558    }
559
560    /// Borrowing token view for text inspection at a one-based offset.
561    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
562        self.lt(offset)
563    }
564
565    /// Token at an absolute buffered index, including hidden/custom channels.
566    ///
567    /// Unlike [`Self::lt`], this does not apply the token stream's channel
568    /// filter and does not move its cursor. It is intended for semantic helpers
569    /// such as automatic-semicolon-insertion checks that inspect trivia
570    /// immediately before the current visible token.
571    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
572        self.input.get(index)
573    }
574
575    /// Current generated rule context, when a generated rule predicate supplied
576    /// one.
577    #[must_use]
578    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
579        self.context
580    }
581
582    /// Flat tree storage containing completed children visible to this hook.
583    #[must_use]
584    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
585        self.tree_storage
586    }
587
588    /// Canonical token store used by completed flat-tree nodes.
589    #[must_use]
590    pub const fn token_store(&self) -> &TokenStore {
591        self.input.token_store()
592    }
593
594    /// Completed parse-tree root ID passed to a replayed action hook.
595    #[must_use]
596    pub const fn tree_id(&self) -> Option<NodeId> {
597        self.tree
598    }
599
600    /// Completed parse tree passed to an action hook, if the action is being
601    /// replayed after recognition.
602    #[must_use]
603    pub fn tree(&self) -> Option<Node<'_>> {
604        self.tree
605            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
606    }
607
608    /// Integer local argument visible to this predicate coordinate.
609    #[must_use]
610    pub fn local_int_arg(&self) -> Option<i64> {
611        self.local_int_arg.map(|(_, value)| value)
612    }
613
614    /// Integer member value observed on the current speculative path.
615    #[must_use]
616    pub fn member_int(&self, member: usize) -> Option<i64> {
617        self.member_values.get(&member).copied()
618    }
619
620    /// Parser action event being replayed, when this context belongs to an
621    /// action hook.
622    #[must_use]
623    pub const fn action(&self) -> Option<ParserAction> {
624        self.action
625    }
626
627    /// Text covered by a parser action event.
628    ///
629    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
630    /// EOF the interval ends at the previous *visible* token, so trailing hidden
631    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
632    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
633    /// itself guards `start > stop`, so an empty interval yields `""`.
634    pub fn action_text(&self) -> String {
635        let Some(action) = self.action else {
636            return String::new();
637        };
638        let Some(stop) = action.stop_index() else {
639            return String::new();
640        };
641        let stop = if self
642            .input
643            .get(stop)
644            .is_some_and(|token| token.token_type() == TOKEN_EOF)
645        {
646            let Some(previous) = self.input.previous_visible_token_index(stop) else {
647                return String::new();
648            };
649            previous
650        } else {
651            stop
652        };
653        self.input.text(action.start_index(), stop)
654    }
655}
656
657/// User extension point for parser semantic predicates and actions that the
658/// metadata generator did not translate into built-in runtime metadata.
659///
660/// Returning `None`/`false` says "not handled", so the runtime falls through
661/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
662/// speculative prediction and must be replay-safe.
663pub trait SemanticHooks {
664    /// Whether generated lexers should route lifecycle callbacks through this
665    /// hook object.
666    ///
667    /// User hook implementations opt in by default. [`NoSemanticHooks`]
668    /// overrides this to keep generated lexers on the direct no-extension
669    /// token path.
670    const ENABLES_LEXER_LIFECYCLE: bool = true;
671
672    /// Whether this hook object may observe parser predicate transitions.
673    ///
674    /// Custom hooks default to conservative predicate handling so the fast
675    /// recognizer does not bypass a `sempred` implementation.
676    fn observes_parser_predicates(&self) -> bool {
677        true
678    }
679
680    /// Whether this hook object may override interpreted parser decisions.
681    ///
682    /// This remains disabled by default so ordinary generated parsers retain
683    /// the fast recognizer path.
684    fn observes_parser_decisions(&self) -> bool {
685        false
686    }
687
688    /// Overrides one interpreted parser decision with a one-based alternative.
689    ///
690    /// Returning `None` leaves normal adaptive prediction in control. Hooks
691    /// that return an alternative own any one-shot or input-index filtering
692    /// they require.
693    fn parser_decision_override(
694        &mut self,
695        decision: usize,
696        input_index: usize,
697        alternative_count: usize,
698    ) -> Option<usize> {
699        let _ = (decision, input_index, alternative_count);
700        None
701    }
702
703    fn sempred<S>(
704        &mut self,
705        ctx: &mut ParserSemCtx<'_, S>,
706        rule_index: usize,
707        pred_index: usize,
708    ) -> Option<bool>
709    where
710        S: TokenSource,
711    {
712        let _ = (ctx, rule_index, pred_index);
713        None
714    }
715
716    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
717    where
718        S: TokenSource,
719    {
720        let _ = (ctx, action);
721        false
722    }
723
724    fn lexer_sempred<I>(
725        &mut self,
726        ctx: &mut LexerSemCtx<'_, I>,
727        rule_index: usize,
728        pred_index: usize,
729    ) -> Option<bool>
730    where
731        I: CharStream,
732    {
733        let _ = (ctx, rule_index, pred_index);
734        None
735    }
736
737    /// Runs a lexer custom action on the committed lexing path. Returns whether
738    /// the hook handled the action.
739    ///
740    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
741    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
742    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
743    /// queued prefix tokens, just like the closure-based `custom_action` API.
744    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
745    /// borrow, so those mutators are inert there.)
746    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
747    where
748        I: CharStream,
749    {
750        let _ = (ctx, action);
751        false
752    }
753
754    /// Runs after runtime-owned lexer state has been reset for reuse.
755    ///
756    /// Implementations should clear extension-owned transient state here.
757    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
758    where
759        I: CharStream,
760    {
761        let _ = ctx;
762    }
763
764    /// Runs before the runtime returns a queued token or starts a new ATN
765    /// token match.
766    ///
767    /// The callback also runs between internal `skip`/`more` matches, so it
768    /// observes every point where another ATN match may start.
769    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
770    where
771        I: CharStream,
772    {
773        let _ = ctx;
774    }
775
776    /// Runs after the accepted path's portable and custom actions, but before
777    /// the token span is finalized and emitted.
778    ///
779    /// Accepted paths that selected `skip` or `more` are included, and the hook
780    /// may observe or override that pending token type.
781    ///
782    /// This callback has no synthetic ATN coordinate. It therefore also runs
783    /// for accepted rules that contain no action or predicate.
784    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
785    where
786        I: CharStream,
787    {
788        let _ = ctx;
789    }
790
791    /// Observes a token after committed lexer actions and portable commands
792    /// have run and the token has been emitted, immediately before it is
793    /// returned to the token stream.
794    ///
795    /// Hidden and custom-channel tokens are included. `skip` and intermediate
796    /// `more` matches do not produce callbacks.
797    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
798        let _ = token;
799    }
800}
801
802/// Default hook object used by parsers that do not need user-supplied
803/// semantics.
804#[derive(Clone, Copy, Debug, Default)]
805pub struct NoSemanticHooks;
806
807impl SemanticHooks for NoSemanticHooks {
808    const ENABLES_LEXER_LIFECYCLE: bool = false;
809
810    fn observes_parser_predicates(&self) -> bool {
811        false
812    }
813}
814
815/// Parser semantic predicate rendered from a supported target template.
816///
817/// The metadata recognizer evaluates these at the token-stream index where the
818/// predicate transition is reached. Unsupported or absent predicate templates
819/// remain unconditional so existing generated parsers keep their previous
820/// behavior unless the generator opts into this table.
821#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
822pub enum ParserPredicate {
823    True,
824    False,
825    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
826    FalseWithMessage {
827        message: &'static str,
828    },
829    /// Target-template test helper that reports predicate evaluation before
830    /// returning the wrapped boolean value.
831    Invoke {
832        value: bool,
833    },
834    LookaheadTextEquals {
835        offset: isize,
836        text: &'static str,
837    },
838    LookaheadNotEquals {
839        offset: isize,
840        token_type: i32,
841    },
842    /// Checks that the last two consumed visible tokens were adjacent in the
843    /// token stream. Used by C# parser predicates for split operator tokens.
844    TokenPairAdjacent,
845    /// Checks a generated parser context child by rule index and text.
846    ///
847    /// If the child is absent the predicate succeeds, matching target helpers
848    /// that treat incomplete or non-matching contexts as non-restrictive.
849    ContextChildRuleTextNotEquals {
850        rule_index: usize,
851        text: &'static str,
852    },
853    /// Compares the current rule invocation's integer argument with a literal
854    /// value from a supported `ValEquals("$i", "...")` target template.
855    LocalIntEquals {
856        value: i64,
857    },
858    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
859    /// rule invocation's integer argument.
860    LocalIntLessOrEqual {
861        value: i64,
862    },
863    /// Compares a generated parser integer member modulo a literal value.
864    MemberModuloEquals {
865        member: usize,
866        modulus: i64,
867        value: i64,
868        equals: bool,
869    },
870    /// Compares a generated parser integer member with a literal value.
871    MemberEquals {
872        member: usize,
873        value: i64,
874        equals: bool,
875    },
876}
877
878impl ParserPredicate {
879    /// Lowers the legacy predicate metadata variant into `SemIR`.
880    ///
881    /// This is the compatibility adapter for generated parsers produced while
882    /// the runtime still emitted closed enum tables. Newer generated parsers
883    /// emit `SemIR` directly.
884    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
885        match self {
886            Self::True => ir.expr(PExpr::Bool(true)),
887            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
888            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
889            Self::LookaheadTextEquals { offset, text } => {
890                let token = ir.expr(PExpr::TokenText(offset));
891                let text = ir.intern(text);
892                let text = ir.expr(PExpr::Str(text));
893                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
894            }
895            Self::LookaheadNotEquals { offset, token_type } => {
896                let actual = ir.expr(PExpr::La(offset));
897                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
898                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
899            }
900            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
901            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
902                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
903                let expected = ir.intern(text);
904                let expected = ir.expr(PExpr::Str(expected));
905                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
906            }
907            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
908            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
909            Self::MemberModuloEquals {
910                member,
911                modulus,
912                value,
913                equals,
914            } => {
915                if modulus == 0 {
916                    return ir.expr(PExpr::Bool(false));
917                }
918                let member = ir.expr(PExpr::Member(member));
919                let modulus = ir.expr(PExpr::Int(modulus));
920                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
921                let expected = ir.expr(PExpr::Int(value));
922                ir.expr(PExpr::Cmp(
923                    if equals { CmpOp::Eq } else { CmpOp::Ne },
924                    actual,
925                    expected,
926                ))
927            }
928            Self::MemberEquals {
929                member,
930                value,
931                equals,
932            } => {
933                let actual = ir.expr(PExpr::Member(member));
934                let expected = ir.expr(PExpr::Int(value));
935                ir.expr(PExpr::Cmp(
936                    if equals { CmpOp::Eq } else { CmpOp::Ne },
937                    actual,
938                    expected,
939                ))
940            }
941        }
942    }
943
944    #[must_use]
945    pub const fn failure_message(self) -> Option<&'static str> {
946        match self {
947            Self::FalseWithMessage { message } => Some(message),
948            Self::True
949            | Self::False
950            | Self::Invoke { .. }
951            | Self::LookaheadTextEquals { .. }
952            | Self::LookaheadNotEquals { .. }
953            | Self::TokenPairAdjacent
954            | Self::ContextChildRuleTextNotEquals { .. }
955            | Self::LocalIntEquals { .. }
956            | Self::LocalIntLessOrEqual { .. }
957            | Self::MemberModuloEquals { .. }
958            | Self::MemberEquals { .. } => None,
959        }
960    }
961}
962
963fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
964    let local = ir.expr(PExpr::LocalArg);
965    let absent = ir.expr(PExpr::IsNull(local));
966    let expected = ir.expr(PExpr::Int(value));
967    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
968    ir.expr(PExpr::Or([absent, comparison].into()))
969}
970
971/// Policy for semantic predicate coordinates that have no runtime
972/// implementation.
973///
974/// ANTLR grammars may embed target-language predicates that the metadata
975/// generator could not translate into a [`ParserPredicate`] table entry. When
976/// recognition reaches such a coordinate the runtime cannot know the grammar
977/// author's intent, so the caller chooses how to proceed.
978///
979/// The default is [`Self::AssumeTrue`], matching the historical behavior of
980/// this runtime. That default is deprecated and will change to [`Self::Error`]
981/// in a future minor release; grammars relying on unconditional predicates
982/// should opt in explicitly.
983#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
984pub enum UnknownSemanticPolicy {
985    /// Treat the predicate as passing, as if it were absent from the grammar.
986    #[default]
987    AssumeTrue,
988    /// Treat the predicate as failing, removing the guarded alternative.
989    AssumeFalse,
990    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
991    /// coordinate that recognition evaluated.
992    Error,
993}
994
995/// Resolves a predicate coordinate that neither a translated table entry nor a
996/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
997///
998/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
999/// so the parse entry can surface every unresolved coordinate afterwards. Both
1000/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
1001/// funnel through here so a missing implementation is never silently coerced
1002/// to a boolean (design goal G1: never silently mis-parse).
1003fn apply_unknown_predicate_policy(
1004    policy: UnknownSemanticPolicy,
1005    rule_index: usize,
1006    pred_index: usize,
1007    hits: &mut Vec<(usize, usize)>,
1008) -> bool {
1009    match policy {
1010        UnknownSemanticPolicy::AssumeTrue => true,
1011        UnknownSemanticPolicy::AssumeFalse => false,
1012        UnknownSemanticPolicy::Error => {
1013            let coordinate = (rule_index, pred_index);
1014            if !hits.contains(&coordinate) {
1015                hits.push(coordinate);
1016            }
1017            false
1018        }
1019    }
1020}
1021
1022/// Interval-set of expected token types, displayable through a vocabulary —
1023/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
1024/// generated test actions.
1025#[derive(Clone, Debug, Eq, PartialEq)]
1026pub struct ExpectedTokenSet {
1027    symbols: BTreeSet<i32>,
1028}
1029
1030impl ExpectedTokenSet {
1031    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
1032    #[must_use]
1033    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
1034        expected_symbols_display(&self.symbols, vocabulary)
1035    }
1036}
1037
1038/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
1039///
1040/// The first syntax error aborts the parse instead of recovering. Generated
1041/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
1042#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1043pub struct BailErrorStrategy;
1044
1045impl BailErrorStrategy {
1046    #[must_use]
1047    pub const fn new() -> Self {
1048        Self
1049    }
1050}
1051
1052/// Prediction strategy requested by generated parser harnesses.
1053#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1054pub enum PredictionMode {
1055    /// Prefer the clean full-context outcome when alternatives reach the same
1056    /// input position.
1057    Ll,
1058    /// Preserve SLL's first-viable alternative bias at a decision, even when a
1059    /// later full-context alternative could avoid recovery.
1060    Sll,
1061    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
1062    LlExactAmbigDetection,
1063}
1064
1065/// Integer argument metadata for a generated parser rule invocation.
1066///
1067/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
1068/// the generator records the rule-transition source state and the value that
1069/// should be visible to semantic predicates inside the callee.
1070#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1071pub struct ParserRuleArg {
1072    /// ATN state containing the rule transition that receives this argument.
1073    pub source_state: usize,
1074    /// Callee rule index for the transition.
1075    pub rule_index: usize,
1076    /// Literal fallback value to expose in the callee.
1077    pub value: i64,
1078    /// Whether the callee should inherit the caller's current integer argument.
1079    pub inherit_local: bool,
1080}
1081
1082/// Integer member mutation attached to an ATN action transition.
1083#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1084pub struct ParserMemberAction {
1085    /// ATN state containing the action transition.
1086    pub source_state: usize,
1087    /// Generator-assigned integer member id.
1088    pub member: usize,
1089    /// Delta applied when the action is reached on one speculative path.
1090    pub delta: i64,
1091}
1092
1093/// Integer return-value assignment attached to an ATN action transition.
1094///
1095/// Generated parsers use this metadata when target actions assign a simple
1096/// return field such as `$y=1000;`. The interpreter applies it while selecting
1097/// the recognized path so the finished parse tree can answer later
1098/// `$label.y` action templates.
1099#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1100pub struct ParserReturnAction {
1101    /// ATN state containing the action transition.
1102    pub source_state: usize,
1103    /// Rule index recorded by the serialized action transition.
1104    pub rule_index: usize,
1105    /// Return-field name as it appears in the grammar.
1106    pub name: &'static str,
1107    /// Literal integer value assigned by the action.
1108    pub value: i64,
1109}
1110
1111impl ParserMemberAction {
1112    /// Lowers this speculative member mutation into a `SemIR` action.
1113    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1114        let delta = ir.expr(PExpr::Int(self.delta));
1115        ParserSemanticAction {
1116            source_state: self.source_state,
1117            rule_index: usize::MAX,
1118            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
1119            speculative: true,
1120        }
1121    }
1122}
1123
1124impl ParserReturnAction {
1125    /// Lowers this committed return-value assignment into a `SemIR` action.
1126    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1127        let name = ir.intern(self.name);
1128        let value = ir.expr(PExpr::Int(self.value));
1129        ParserSemanticAction {
1130            source_state: self.source_state,
1131            rule_index: self.rule_index,
1132            stmt: ir.stmt(AStmt::SetReturn(name, value)),
1133            speculative: false,
1134        }
1135    }
1136}
1137
1138/// Parser predicate coordinate lowered into [`SemIr`].
1139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1140pub struct ParserSemanticPredicate {
1141    /// Serialized rule index that owns this predicate.
1142    pub rule_index: usize,
1143    /// Predicate index inside the owning rule.
1144    pub pred_index: usize,
1145    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1146    pub expr: ExprId,
1147    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1148    pub failure_message: Option<&'static str>,
1149}
1150
1151/// Parser action coordinate lowered into [`SemIr`].
1152#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1153pub struct ParserSemanticAction {
1154    /// ATN state containing the action transition.
1155    pub source_state: usize,
1156    /// Serialized rule index recorded by the action transition.
1157    pub rule_index: usize,
1158    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1159    pub stmt: StmtId,
1160    /// Whether this action may run on speculative recognition paths.
1161    pub speculative: bool,
1162}
1163
1164/// Data-driven semantic tables emitted by generated parsers.
1165///
1166/// This is the runtime representation for issue #9's `SemIR` path. Existing
1167/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1168/// remain accepted as deprecated adapters for generated code produced before
1169/// this table existed.
1170#[derive(Clone, Debug, Default, Eq, PartialEq)]
1171pub struct ParserSemantics {
1172    pub ir: SemIr,
1173    pub predicates: Vec<ParserSemanticPredicate>,
1174    pub actions: Vec<ParserSemanticAction>,
1175}
1176
1177/// Optional generated-runtime metadata for metadata-driven parser execution.
1178#[derive(Clone, Copy, Debug, Default)]
1179pub struct ParserRuntimeOptions<'a> {
1180    /// Rule indexes whose `@init` actions should be replayed.
1181    pub init_action_rules: &'a [usize],
1182    /// Whether generated parse-tree contexts should retain alternative numbers.
1183    pub track_alt_numbers: bool,
1184    /// Whether generated typed contexts should retain private dispatch alternatives.
1185    ///
1186    /// Unlike `track_alt_numbers`, this metadata does not affect the public
1187    /// alternative number or parse-tree rendering.
1188    #[doc(hidden)]
1189    pub track_context_alt_numbers: bool,
1190    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1191    pub predicates: &'a [(usize, usize, ParserPredicate)],
1192    /// `SemIR` predicate/action table emitted by newer generated parsers.
1193    pub semantics: Option<&'a ParserSemantics>,
1194    /// Rule-call integer argument table keyed by ATN source state.
1195    pub rule_args: &'a [ParserRuleArg],
1196    /// Integer member mutations keyed by ATN action source state.
1197    pub member_actions: &'a [ParserMemberAction],
1198    /// Integer return assignments keyed by ATN action source state.
1199    pub return_actions: &'a [ParserReturnAction],
1200    /// How to evaluate semantic predicate coordinates absent from
1201    /// `predicates`.
1202    pub unknown_predicate_policy: UnknownSemanticPolicy,
1203}
1204
1205pub trait Parser: Recognizer {
1206    /// Reports whether generated parser rules should build parse-tree nodes
1207    /// while recognizing input.
1208    fn build_parse_trees(&self) -> bool;
1209
1210    /// Enables or disables parse-tree construction for subsequent rule calls.
1211    fn set_build_parse_trees(&mut self, build: bool);
1212
1213    /// Returns the number of parser syntax errors recorded by committed parse
1214    /// paths so far.
1215    fn number_of_syntax_errors(&self) -> usize {
1216        0
1217    }
1218
1219    /// Reports whether prediction diagnostic-listener messages are emitted
1220    /// during parser ATN recognition.
1221    fn report_diagnostic_errors(&self) -> bool {
1222        false
1223    }
1224
1225    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1226    /// rule calls.
1227    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1228
1229    /// Reports the prediction strategy used when selecting among alternatives.
1230    fn prediction_mode(&self) -> PredictionMode {
1231        PredictionMode::Ll
1232    }
1233
1234    /// Sets the prediction strategy for subsequent rule calls.
1235    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1236
1237    /// Maximum rule-nesting depth accepted before the parse aborts, or `None`
1238    /// for unlimited (the default).
1239    fn max_rule_depth(&self) -> Option<usize> {
1240        None
1241    }
1242
1243    /// Bounds the rule-nesting depth for subsequent rule calls.
1244    ///
1245    /// Deeply nested input is parsed safely regardless (rule recursion grows
1246    /// onto a segmented stack), but each nesting level still costs CPU and
1247    /// tree memory. Callers parsing untrusted input can cap that work: when
1248    /// the limit is exceeded the parse stops with a positioned syntax error
1249    /// instead of consuming unbounded resources. The measure counts rule
1250    /// frames plus left-recursive operator expansions, matching what an
1251    /// upstream-ANTLR rule-entry listener observes.
1252    ///
1253    /// The cap is enforced by generated recursive-descent rule bodies. When
1254    /// one is set, generated dispatch routes ATN-preferred rules through
1255    /// their generated bodies too, trading that fast path for enforcement.
1256    /// Rules the generator emitted no body for (interpreter-only fallback)
1257    /// do not check the cap.
1258    fn set_max_rule_depth(&mut self, _depth: Option<usize>) {}
1259
1260    /// Registers a listener for committed rule enter/exit events during
1261    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
1262    /// the delivery contract. The default implementation drops the listener;
1263    /// [`BaseParser`] and generated parsers deliver events.
1264    fn add_parse_listener(&mut self, _listener: Box<dyn ParseListener>) {}
1265
1266    /// Removes every registered parse listener and returns them, dropping
1267    /// any sticky abort a removed listener had requested.
1268    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
1269        Vec::new()
1270    }
1271}
1272
1273#[derive(Debug)]
1274struct LeftRecursiveCallerOverlap {
1275    atn_key: SharedAtnCacheKey,
1276    state_number: usize,
1277    symbol: i32,
1278    context_version: usize,
1279    overlaps: bool,
1280}
1281
1282const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1283
1284#[derive(Debug)]
1285pub struct BaseParser<S, H = NoSemanticHooks> {
1286    input: CommonTokenStream<S>,
1287    tree: ParseTreeStorage,
1288    data: RecognizerData,
1289    semantic_hooks: H,
1290    decision_override_generation: usize,
1291    build_parse_trees: bool,
1292    syntax_errors: usize,
1293    report_diagnostic_errors: bool,
1294    prediction_mode: PredictionMode,
1295    prediction_diagnostics: Vec<ParserDiagnostic>,
1296    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1297    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1298    generated_sync_expected: Option<TokenBitSet>,
1299    generated_recovery_error_index: Option<usize>,
1300    generated_recovery_error_states: BTreeSet<isize>,
1301    int_members: BTreeMap<usize, i64>,
1302    rule_context_stack: Vec<RuleContextFrame>,
1303    rule_context_version: usize,
1304    left_recursive_caller_overlap_cache:
1305        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1306    pending_invoking_states: Vec<isize>,
1307    precedence_stack: Vec<i32>,
1308    /// Predicate side effects are observable in a few target-template tests;
1309    /// speculative recognition may revisit the same coordinate, so replay it
1310    /// once per parser instance.
1311    invoked_predicates: Vec<(usize, usize)>,
1312    /// Bail error strategy: the first syntax error aborts the parse instead of
1313    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1314    /// through `set_error_handler(BailErrorStrategy::new())`.
1315    bail_on_error: bool,
1316    /// Parse listeners receiving committed rule enter/exit events during
1317    /// recognition (ANTLR's `addParseListener`). Empty in the default
1318    /// configuration, and every dispatch site is gated on emptiness so the
1319    /// unused feature costs one predictable branch per rule boundary.
1320    parse_listeners: Vec<ParseListenerSlot>,
1321    /// Sticky abort requested by a parse listener's `enter_every_rule`.
1322    /// Mirrors `rule_depth_error`: rule-level recovery absorbs the error like
1323    /// any rule failure, so the flag stays set until the top-level entry
1324    /// drains it and fails the parse.
1325    parse_listener_abort: Option<AntlrError>,
1326    /// Optional cap on rule-nesting depth for adversarial-input hardening.
1327    /// `None` (default) parses unbounded nesting; `Some(n)` aborts the parse
1328    /// with a positioned syntax error once `n` rule frames are exceeded.
1329    max_rule_depth: Option<usize>,
1330    /// Sticky depth-cap violation. Rule-level recovery would otherwise absorb
1331    /// the error and keep parsing; once set, every subsequent rule entry fails
1332    /// immediately and the top-level entry returns this error even when
1333    /// recovery produced a tree.
1334    rule_depth_error: Option<AntlrError>,
1335    /// Left-recursive expansions currently deepening the parse tree. Each
1336    /// operator iteration wraps the previous context one level deeper without
1337    /// pushing a rule frame, so the depth cap must count these separately —
1338    /// upstream ANTLR fires a rule-entry listener event for exactly this case
1339    /// (`Parser.pushNewRecursionContext` → `triggerEnterRuleEvent`).
1340    recursion_expansions: usize,
1341    /// Per-invocation snapshots of [`Self::recursion_expansions`], pushed by
1342    /// `enter_recursion_rule` and restored by `unroll_recursion_context`, so a
1343    /// finished left-recursive rule releases the depth its expansions added.
1344    recursion_expansion_marks: Vec<usize>,
1345    /// How to evaluate predicate coordinates missing from the active
1346    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1347    unknown_predicate_policy: UnknownSemanticPolicy,
1348    /// Unknown predicate coordinates evaluated by the current parse, recorded
1349    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1350    unknown_predicate_hits: Vec<(usize, usize)>,
1351    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1352    /// that no hook handled, recorded so a generated `hook`/error-disposed
1353    /// action fails loud instead of being silently dropped. Keyed by
1354    /// `(rule_index, source_state)`.
1355    unhandled_action_hits: Vec<(usize, usize)>,
1356    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1357    /// hot rule-transition checks to a vector lookup after the first visit
1358    /// while the thread-local shared ATN cache still owns the cross-parse
1359    /// computed value.
1360    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1361    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1362    /// epsilon-reachable consuming transition and shows up as a hot loop in
1363    /// `next_recovery_context` and recovery diagnostics on long inputs.
1364    /// Keying on `state_number` and sharing the result through `Rc` removes
1365    /// repeated DFS plus per-call `BTreeSet` allocations.
1366    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1367    /// Same expected-symbol cache as a bitset for generated parser sync.
1368    /// Successful parses only need `contains` and union; keeping that path out
1369    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1370    /// check and defers deterministic formatting to diagnostics.
1371    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1372    /// Per-state cache for whether a return state can finish its owning rule
1373    /// without consuming more input. Generated-parser sync uses this to walk
1374    /// parent prediction contexts for nullable exits without paying repeated
1375    /// epsilon-closure searches on every loop or optional decision.
1376    rule_stop_reach_cache: Vec<Option<bool>>,
1377    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1378    /// threads the same epsilon-recovery context through hundreds of follow
1379    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1380    /// reference bump and lets the memo key hash by pointer.
1381    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1382    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1383    /// touch a given decision state still pay no upfront cost; once cached,
1384    /// the recognizer prunes alternatives whose look-1 cannot accept the
1385    /// current lookahead, letting common SLL decisions reduce to a single
1386    /// transition walk instead of a full speculative fan-out.
1387    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1388    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1389    /// Each multi-trans visit asks "given this decision state and this
1390    /// lookahead token, which alt do I commit to?" Hitting this cache
1391    /// turns the question into a hashmap probe instead of re-scanning
1392    /// the decision's per-transition FIRST sets every visit.
1393    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1394    /// Predicate results shared by the fast recognizer's clean and recovery
1395    /// attempts. The eligible fast path keeps every runtime-provided input
1396    /// fixed, and custom predicate hooks are required to be replay-safe.
1397    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1398    /// Cache for whether an ATN state can reach itself without consuming
1399    /// input. Only those states need the recursive recognizer's
1400    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1401    /// grammar-static cache survive parser resets without reusing state
1402    /// coordinates after the parser is driven against a different ATN.
1403    empty_cycle_cache: Vec<Option<bool>>,
1404    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1405    /// Probe state for deciding whether clean-pass memo entries are worth
1406    /// storing for the current parse.
1407    clean_memo_mode: CleanMemoMode,
1408    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1409    clean_memo_probe_samples: usize,
1410    clean_memo_probe_repeats: usize,
1411    clean_memo_sparse_samples: usize,
1412    /// Reusable cycle and memo storage for one top-level fast recognition.
1413    fast_recognize_scratch: FastRecognizeTopScratch,
1414    /// Reusable direct-index/hash storage for clean speculative endpoints.
1415    fast_outcome_dedup: FastOutcomeDedupScratch,
1416    /// Empty recovery-symbols singleton used as the default at rule entry and
1417    /// after token consumption.
1418    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1419    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1420    /// prefilter trims speculative rule calls whose called rule cannot
1421    /// match the current lookahead, but it also bypasses single-token
1422    /// insertion / deletion recovery that ANTLR runs at the rule's first
1423    /// consuming transition. `parse_atn_rule` flips this off and retries
1424    /// when the first pass produces no clean outcome so the runtime can
1425    /// repair inputs the reference parser would have repaired.
1426    fast_first_set_prefilter: bool,
1427    /// Whether the fast recognizer should explore parser error-recovery paths.
1428    /// Public rule parsing starts with this disabled for the common valid-input
1429    /// path and enables it only for the retry that needs ANTLR-style repairs.
1430    fast_recovery_enabled: bool,
1431    /// Whether the fast recognizer should record terminal-token nodes while
1432    /// speculating. Clean valid-input parsing can reconstruct terminals from
1433    /// selected rule spans after recognition, avoiding many speculative
1434    /// nodes that are thrown away with losing paths.
1435    fast_token_nodes_enabled: bool,
1436    /// Whether fast recognition should retain private/public rule alternatives
1437    /// in deferred tree metadata.
1438    fast_track_alt_numbers: bool,
1439    /// Parser-owned append-only storage for speculative recognition output.
1440    /// Each public interpreted-rule entry clears lengths while retaining
1441    /// bounded backing capacities for parser reuse.
1442    recognition_arena: RecognitionArena,
1443    last_recognition_arena_root: NodeSeqId,
1444    last_recognition_arena_diagnostics: DiagnosticSeqId,
1445}
1446
1447/// Rollback marker for speculative generated parser paths.
1448#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1449pub struct GeneratedDiagnosticsCheckpoint {
1450    diagnostics_len: usize,
1451    syntax_errors: usize,
1452    tree: ParseTreeCheckpoint,
1453}
1454
1455/// Storage and reachability counters for the most recent interpreted-rule
1456/// recognition arena.
1457#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1458pub struct RecognitionArenaStats {
1459    pub total_nodes: usize,
1460    pub live_nodes: usize,
1461    pub dead_nodes: usize,
1462    pub node_capacity: usize,
1463    pub total_links: usize,
1464    pub live_links: usize,
1465    pub dead_links: usize,
1466    pub link_capacity: usize,
1467    pub total_extras: usize,
1468    pub live_extras: usize,
1469    pub dead_extras: usize,
1470    pub extra_capacity: usize,
1471}
1472
1473#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1474struct RuleContextFrame {
1475    rule_index: usize,
1476    invoking_state: isize,
1477}
1478
1479#[derive(Clone, Debug, Eq, PartialEq)]
1480struct RecognizeOutcome {
1481    index: usize,
1482    consumed_eof: bool,
1483    alt_number: usize,
1484    member_values: BTreeMap<usize, i64>,
1485    return_values: BTreeMap<String, i64>,
1486    diagnostics: DiagnosticSeqId,
1487    decisions: Vec<usize>,
1488    actions: Vec<ParserAction>,
1489    nodes: NodeSeqId,
1490}
1491
1492#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1493struct FastRecognizeOutcome {
1494    index: usize,
1495    consumed_eof: bool,
1496    diagnostics: DiagnosticSeqId,
1497    deferred_nodes: FastDeferredNodeId,
1498    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1499    /// Copying an outcome copies this compact ID; prepending appends one
1500    /// `SeqLink` without allocating an individual node or list tail.
1501    nodes: NodeSeqId,
1502}
1503
1504#[derive(Debug, Default)]
1505struct FastRecognizeTopScratch {
1506    visiting: FxHashSet<FastRecognizeKey>,
1507    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1508}
1509
1510impl FastRecognizeTopScratch {
1511    fn prepare(&mut self, memo_capacity: usize) {
1512        self.visiting.clear();
1513        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1514        self.memo.clear();
1515        self.memo.reserve(memo_capacity);
1516    }
1517
1518    fn release_oversized_memo(&mut self) {
1519        self.memo.clear();
1520        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1521            self.memo = FxHashMap::default();
1522        }
1523    }
1524}
1525
1526fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1527    buffered_tokens.saturating_mul(8).clamp(
1528        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1529        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1530    )
1531}
1532
1533#[derive(Debug, Default)]
1534struct FastOutcomeDedupScratch {
1535    dense_words: Vec<u64>,
1536    touched_dense_words: Vec<u32>,
1537    sparse_keys: FxHashSet<(usize, bool)>,
1538}
1539
1540/// Handle into the parser-owned deferred tree rope.
1541///
1542/// The sentinel keeps outcomes and repetition paths compact without an
1543/// `Option` discriminant or per-node reference counting.
1544#[repr(transparent)]
1545#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1546struct FastDeferredNodeId(u32);
1547
1548impl FastDeferredNodeId {
1549    const EMPTY: Self = Self(u32::MAX);
1550
1551    const fn is_empty(self) -> bool {
1552        self.0 == Self::EMPTY.0
1553    }
1554}
1555
1556impl Default for FastDeferredNodeId {
1557    fn default() -> Self {
1558        Self::EMPTY
1559    }
1560}
1561
1562#[repr(transparent)]
1563#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1564struct FastDeferredRuleId(u32);
1565
1566/// One immutable deferred-tree rope record in `RecognitionArena`.
1567#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1568enum FastDeferredNode {
1569    Fragment(NodeSeqId),
1570    Rule(FastDeferredRuleId),
1571    Alternative(u32),
1572    LeftRecursiveBoundary {
1573        rule_index: u32,
1574    },
1575    Concat {
1576        prefix: FastDeferredNodeId,
1577        suffix: FastDeferredNodeId,
1578    },
1579}
1580
1581#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1582struct FastDeferredRule {
1583    rule_index: u32,
1584    invoking_state: i32,
1585    start_index: u32,
1586    stop_index: Option<u32>,
1587    deferred_children: FastDeferredNodeId,
1588    children: NodeSeqId,
1589}
1590
1591#[repr(transparent)]
1592#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1593struct RecognizedNodeId(u32);
1594
1595#[repr(transparent)]
1596#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1597struct NodeSeqId(u32);
1598
1599impl NodeSeqId {
1600    const EMPTY: Self = Self(u32::MAX);
1601
1602    const fn is_empty(self) -> bool {
1603        self.0 == Self::EMPTY.0
1604    }
1605}
1606
1607impl Default for NodeSeqId {
1608    fn default() -> Self {
1609        Self::EMPTY
1610    }
1611}
1612
1613#[repr(transparent)]
1614#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1615struct DiagnosticSeqId(u32);
1616
1617impl DiagnosticSeqId {
1618    const EMPTY: Self = Self(u32::MAX);
1619
1620    const fn is_empty(self) -> bool {
1621        self.0 == Self::EMPTY.0
1622    }
1623}
1624
1625impl Default for DiagnosticSeqId {
1626    fn default() -> Self {
1627        Self::EMPTY
1628    }
1629}
1630
1631#[repr(transparent)]
1632#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1633struct RecognitionExtraId(u32);
1634
1635#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1636struct SeqLink {
1637    head: RecognizedNodeId,
1638    tail: NodeSeqId,
1639}
1640
1641#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1642struct DiagnosticLink {
1643    head: RecognitionExtraId,
1644    tail: DiagnosticSeqId,
1645}
1646
1647struct ArenaRuleSpec {
1648    rule_index: usize,
1649    invoking_state: isize,
1650    alt_number: usize,
1651    start_index: usize,
1652    stop_index: Option<usize>,
1653    return_values: BTreeMap<String, i64>,
1654    children: NodeSeqId,
1655}
1656
1657/// Compact speculative node record. Common records contain only IDs and
1658/// scalars; missing-token text and generated return values live in `extras`.
1659#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1660enum ArenaRecognizedNode {
1661    Token {
1662        token: TokenId,
1663    },
1664    ErrorToken {
1665        token: TokenId,
1666    },
1667    MissingToken {
1668        extra: RecognitionExtraId,
1669    },
1670    Rule {
1671        rule_index: u32,
1672        invoking_state: i32,
1673        alt_number: u32,
1674        start_index: u32,
1675        stop_index: Option<u32>,
1676        return_values: Option<RecognitionExtraId>,
1677        children: NodeSeqId,
1678    },
1679    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1680    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1681    /// public rule entry hands the tree to the caller.
1682    LeftRecursiveBoundary {
1683        rule_index: u32,
1684        alt_number: u32,
1685    },
1686}
1687
1688#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1689enum RecognitionExtra {
1690    MissingToken {
1691        token_type: i32,
1692        at_index: u32,
1693        text: String,
1694    },
1695    ReturnValues(BTreeMap<String, i64>),
1696    Diagnostic(ParserDiagnostic),
1697}
1698
1699#[derive(Debug, Default)]
1700struct RecognitionArena {
1701    nodes: Vec<ArenaRecognizedNode>,
1702    seq_links: Vec<SeqLink>,
1703    diagnostic_links: Vec<DiagnosticLink>,
1704    extras: Vec<RecognitionExtra>,
1705    deferred_nodes: Vec<FastDeferredNode>,
1706    deferred_rules: Vec<FastDeferredRule>,
1707}
1708
1709// Preserve normal parser reuse while preventing one pathological parse from
1710// pinning an arbitrarily large arena for the parser's remaining lifetime.
1711const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1712const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1713const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1714const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1715const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1716const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1717
1718impl RecognitionArena {
1719    fn reset(&mut self) {
1720        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1721        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1722        reset_arena_vec(
1723            &mut self.diagnostic_links,
1724            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1725        );
1726        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1727        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1728        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1729    }
1730
1731    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1732        let id = RecognizedNodeId(
1733            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1734        );
1735        self.nodes.push(node);
1736        id
1737    }
1738
1739    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1740        let id = RecognitionExtraId(
1741            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1742        );
1743        self.extras.push(extra);
1744        id
1745    }
1746
1747    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1748        let id = NodeSeqId(
1749            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1750        );
1751        self.seq_links.push(SeqLink { head, tail });
1752        id
1753    }
1754
1755    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1756        let id = FastDeferredNodeId(
1757            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1758        );
1759        self.deferred_nodes.push(node);
1760        id
1761    }
1762
1763    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1764        let id = FastDeferredRuleId(
1765            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1766        );
1767        self.deferred_rules.push(rule);
1768        id
1769    }
1770
1771    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1772        if nodes.is_empty() {
1773            FastDeferredNodeId::EMPTY
1774        } else {
1775            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1776        }
1777    }
1778
1779    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1780        let rule = self.push_deferred_rule(rule);
1781        self.push_deferred_node(FastDeferredNode::Rule(rule))
1782    }
1783
1784    fn deferred_alternative(&mut self, alt_number: usize) -> FastDeferredNodeId {
1785        self.push_deferred_node(FastDeferredNode::Alternative(
1786            u32::try_from(alt_number).expect("alternative number fits in u32"),
1787        ))
1788    }
1789
1790    fn deferred_left_recursive_boundary(&mut self, rule_index: usize) -> FastDeferredNodeId {
1791        self.push_deferred_node(FastDeferredNode::LeftRecursiveBoundary {
1792            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
1793        })
1794    }
1795
1796    fn concat_deferred_nodes(
1797        &mut self,
1798        prefix: FastDeferredNodeId,
1799        suffix: FastDeferredNodeId,
1800    ) -> FastDeferredNodeId {
1801        if prefix.is_empty() {
1802            return suffix;
1803        }
1804        if suffix.is_empty() {
1805            return prefix;
1806        }
1807        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1808    }
1809
1810    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1811        self.deferred_nodes[id.0 as usize]
1812    }
1813
1814    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1815        self.deferred_rules[id.0 as usize]
1816    }
1817
1818    fn prepend_diagnostic(
1819        &mut self,
1820        tail: DiagnosticSeqId,
1821        diagnostic: ParserDiagnostic,
1822    ) -> DiagnosticSeqId {
1823        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1824        self.prepend_diagnostic_id(tail, head)
1825    }
1826
1827    fn prepend_diagnostic_id(
1828        &mut self,
1829        tail: DiagnosticSeqId,
1830        head: RecognitionExtraId,
1831    ) -> DiagnosticSeqId {
1832        let id = DiagnosticSeqId(
1833            u32::try_from(self.diagnostic_links.len())
1834                .expect("diagnostic sequence arena fits in u32"),
1835        );
1836        self.diagnostic_links.push(DiagnosticLink { head, tail });
1837        id
1838    }
1839
1840    fn concat_diagnostics(
1841        &mut self,
1842        prefix: DiagnosticSeqId,
1843        mut suffix: DiagnosticSeqId,
1844    ) -> DiagnosticSeqId {
1845        if prefix.is_empty() {
1846            return suffix;
1847        }
1848        if suffix.is_empty() {
1849            return prefix;
1850        }
1851        let mut reversed = DiagnosticSeqId::EMPTY;
1852        let mut cursor = prefix;
1853        while let Some(link) = self.diagnostic_link(cursor) {
1854            reversed = self.prepend_diagnostic_id(reversed, link.head);
1855            cursor = link.tail;
1856        }
1857        while let Some(link) = self.diagnostic_link(reversed) {
1858            suffix = self.prepend_diagnostic_id(suffix, link.head);
1859            reversed = link.tail;
1860        }
1861        suffix
1862    }
1863
1864    #[cfg(test)]
1865    fn diagnostic_sequence(
1866        &mut self,
1867        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1868    ) -> DiagnosticSeqId {
1869        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1870        let mut sequence = DiagnosticSeqId::EMPTY;
1871        for diagnostic in diagnostics.into_iter().rev() {
1872            sequence = self.prepend_diagnostic(sequence, diagnostic);
1873        }
1874        sequence
1875    }
1876
1877    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1878        self.nodes[id.0 as usize]
1879    }
1880
1881    fn set_boundary_alt_number(&mut self, id: RecognizedNodeId, alt_number: u32) {
1882        let ArenaRecognizedNode::LeftRecursiveBoundary {
1883            alt_number: stored, ..
1884        } = &mut self.nodes[id.0 as usize]
1885        else {
1886            unreachable!("deferred boundary must materialize as a boundary node");
1887        };
1888        *stored = alt_number;
1889    }
1890
1891    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1892        &self.extras[id.0 as usize]
1893    }
1894
1895    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1896        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1897    }
1898
1899    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1900        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1901    }
1902
1903    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1904        NodeSeqIter {
1905            arena: self,
1906            cursor: sequence,
1907        }
1908    }
1909
1910    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1911        DiagnosticSeqIter {
1912            arena: self,
1913            cursor: sequence,
1914        }
1915    }
1916
1917    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1918        self.diagnostics(sequence).count()
1919    }
1920
1921    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1922        self.diagnostics(sequence)
1923            .filter(|diagnostic| {
1924                diagnostic.message.starts_with("mismatched input ")
1925                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1926            })
1927            .count()
1928    }
1929
1930    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1931        self.diagnostics(left).cmp(self.diagnostics(right))
1932    }
1933
1934    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1935        self.iter(sequence).count()
1936    }
1937
1938    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1939        self.iter(sequence).any(|node| match self.node(node) {
1940            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1941            ArenaRecognizedNode::Rule { children, .. } => {
1942                self.sequence_has_left_recursive_boundary(children)
1943            }
1944            ArenaRecognizedNode::Token { .. }
1945            | ArenaRecognizedNode::ErrorToken { .. }
1946            | ArenaRecognizedNode::MissingToken { .. } => false,
1947        })
1948    }
1949
1950    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1951        self.iter(sequence).any(|node| {
1952            matches!(
1953                self.node(node),
1954                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1955            )
1956        })
1957    }
1958
1959    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1960        self.iter(sequence).any(|node| {
1961            matches!(
1962                self.node(node),
1963                ArenaRecognizedNode::Token { .. }
1964                    | ArenaRecognizedNode::ErrorToken { .. }
1965                    | ArenaRecognizedNode::MissingToken { .. }
1966            )
1967        })
1968    }
1969
1970    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1971        match self.node(node) {
1972            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1973                Some(token.index())
1974            }
1975            ArenaRecognizedNode::MissingToken { extra } => {
1976                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1977                    unreachable!("missing-token node must reference missing-token extra");
1978                };
1979                Some(*at_index as usize)
1980            }
1981            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1982            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1983        }
1984    }
1985
1986    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
1987        match self.node(node) {
1988            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1989                Some(token.index())
1990            }
1991            ArenaRecognizedNode::MissingToken { extra } => {
1992                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1993                    unreachable!("missing-token node must reference missing-token extra");
1994                };
1995                (*at_index as usize).checked_sub(1)
1996            }
1997            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
1998            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1999        }
2000    }
2001
2002    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
2003        let start = self.node_start_index(node)?;
2004        let stop = self.node_stop_index(node);
2005        Some((start, stop))
2006    }
2007
2008    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
2009        self.iter(sequence)
2010            .find_map(|node| self.node_start_index(node))
2011    }
2012
2013    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
2014        let mut stop = None;
2015        for node in self.iter(sequence) {
2016            if let Some(index) = self.node_stop_index(node) {
2017                stop = Some(index);
2018            }
2019        }
2020        stop
2021    }
2022
2023    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
2024        self.iter(sequence)
2025            .any(|node| self.node_needs_stable_tie(node))
2026    }
2027
2028    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
2029        match self.node(node) {
2030            ArenaRecognizedNode::Token { .. }
2031            | ArenaRecognizedNode::ErrorToken { .. }
2032            | ArenaRecognizedNode::MissingToken { .. } => false,
2033            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
2034            ArenaRecognizedNode::Rule {
2035                rule_index,
2036                children,
2037                ..
2038            } => self.iter(children).any(|child| {
2039                matches!(
2040                    self.node(child),
2041                    ArenaRecognizedNode::Rule {
2042                        rule_index: child_rule,
2043                        ..
2044                    } if child_rule == rule_index
2045                ) || self.node_needs_stable_tie(child)
2046            }),
2047        }
2048    }
2049
2050    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
2051        loop {
2052            match (self.link(left), self.link(right)) {
2053                (Some(left_link), Some(right_link)) => {
2054                    let order = self.compare_nodes(left_link.head, right_link.head);
2055                    if order != Ordering::Equal {
2056                        return order;
2057                    }
2058                    left = left_link.tail;
2059                    right = right_link.tail;
2060                }
2061                (None, None) => return Ordering::Equal,
2062                (None, Some(_)) => return Ordering::Less,
2063                (Some(_), None) => return Ordering::Greater,
2064            }
2065        }
2066    }
2067
2068    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
2069        let left = self.node(left);
2070        let right = self.node(right);
2071        match (left, right) {
2072            (
2073                ArenaRecognizedNode::Token { token: left },
2074                ArenaRecognizedNode::Token { token: right },
2075            )
2076            | (
2077                ArenaRecognizedNode::ErrorToken { token: left },
2078                ArenaRecognizedNode::ErrorToken { token: right },
2079            ) => left.cmp(&right),
2080            (
2081                ArenaRecognizedNode::MissingToken { extra: left },
2082                ArenaRecognizedNode::MissingToken { extra: right },
2083            ) => self.extra(left).cmp(self.extra(right)),
2084            (
2085                ArenaRecognizedNode::Rule {
2086                    rule_index: left_rule,
2087                    invoking_state: left_invoking,
2088                    alt_number: left_alt,
2089                    start_index: left_start,
2090                    stop_index: left_stop,
2091                    return_values: left_returns,
2092                    children: left_children,
2093                },
2094                ArenaRecognizedNode::Rule {
2095                    rule_index: right_rule,
2096                    invoking_state: right_invoking,
2097                    alt_number: right_alt,
2098                    start_index: right_start,
2099                    stop_index: right_stop,
2100                    return_values: right_returns,
2101                    children: right_children,
2102                },
2103            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
2104                .cmp(&(
2105                    right_rule,
2106                    right_invoking,
2107                    right_alt,
2108                    right_start,
2109                    right_stop,
2110                ))
2111                .then_with(|| {
2112                    left_returns
2113                        .map(|id| self.extra(id))
2114                        .cmp(&right_returns.map(|id| self.extra(id)))
2115                })
2116                .then_with(|| self.compare_sequences(left_children, right_children)),
2117            (
2118                ArenaRecognizedNode::LeftRecursiveBoundary {
2119                    rule_index: left_rule,
2120                    alt_number: left_alt,
2121                },
2122                ArenaRecognizedNode::LeftRecursiveBoundary {
2123                    rule_index: right_rule,
2124                    alt_number: right_alt,
2125                },
2126            ) => (left_rule, left_alt).cmp(&(right_rule, right_alt)),
2127            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
2128        }
2129    }
2130
2131    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
2132        let mut reversed = NodeSeqId::EMPTY;
2133        while let Some(link) = self.link(sequence) {
2134            reversed = self.prepend(reversed, link.head);
2135            sequence = link.tail;
2136        }
2137        reversed
2138    }
2139
2140    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
2141        if !self.sequence_has_direct_boundary(sequence) {
2142            return sequence;
2143        }
2144        let mut reversed = NodeSeqId::EMPTY;
2145        while let Some(link) = self.link(sequence) {
2146            match self.node(link.head) {
2147                ArenaRecognizedNode::LeftRecursiveBoundary {
2148                    rule_index,
2149                    alt_number,
2150                } => {
2151                    if !reversed.is_empty() {
2152                        let children = self.reverse_sequence(reversed);
2153                        let start_index = self.sequence_start_index(children).unwrap_or_default();
2154                        let stop_index = self.sequence_stop_index(children);
2155                        let rule = self.push_node(ArenaRecognizedNode::Rule {
2156                            rule_index,
2157                            invoking_state: -1,
2158                            alt_number,
2159                            start_index: u32::try_from(start_index)
2160                                .expect("left-recursive start index fits in u32"),
2161                            stop_index: stop_index.map(|index| {
2162                                u32::try_from(index).expect("left-recursive stop index fits in u32")
2163                            }),
2164                            return_values: None,
2165                            children,
2166                        });
2167                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
2168                    }
2169                }
2170                _ => {
2171                    reversed = self.prepend(reversed, link.head);
2172                }
2173            }
2174            sequence = link.tail;
2175        }
2176        self.reverse_sequence(reversed)
2177    }
2178
2179    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
2180        let mut live_nodes = vec![false; self.nodes.len()];
2181        let mut live_links = vec![false; self.seq_links.len()];
2182        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
2183        let mut live_extras = vec![false; self.extras.len()];
2184        let mut pending = vec![root];
2185        while let Some(mut sequence) = pending.pop() {
2186            while let Some(link) = self.link(sequence) {
2187                let link_index = sequence.0 as usize;
2188                if live_links[link_index] {
2189                    break;
2190                }
2191                live_links[link_index] = true;
2192                let node_index = link.head.0 as usize;
2193                if !live_nodes[node_index] {
2194                    live_nodes[node_index] = true;
2195                    match self.node(link.head) {
2196                        ArenaRecognizedNode::MissingToken { extra } => {
2197                            live_extras[extra.0 as usize] = true;
2198                        }
2199                        ArenaRecognizedNode::Rule {
2200                            return_values,
2201                            children,
2202                            ..
2203                        } => {
2204                            if let Some(extra) = return_values {
2205                                live_extras[extra.0 as usize] = true;
2206                            }
2207                            pending.push(children);
2208                        }
2209                        ArenaRecognizedNode::Token { .. }
2210                        | ArenaRecognizedNode::ErrorToken { .. }
2211                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
2212                    }
2213                }
2214                sequence = link.tail;
2215            }
2216        }
2217        let mut diagnostics = diagnostics;
2218        while let Some(link) = self.diagnostic_link(diagnostics) {
2219            let link_index = diagnostics.0 as usize;
2220            if live_diagnostic_links[link_index] {
2221                break;
2222            }
2223            live_diagnostic_links[link_index] = true;
2224            live_extras[link.head.0 as usize] = true;
2225            diagnostics = link.tail;
2226        }
2227        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
2228        let live_link_count = live_links.into_iter().filter(|live| *live).count()
2229            + live_diagnostic_links
2230                .into_iter()
2231                .filter(|live| *live)
2232                .count();
2233        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
2234        let total_links = self.seq_links.len() + self.diagnostic_links.len();
2235        RecognitionArenaStats {
2236            total_nodes: self.nodes.len(),
2237            live_nodes: live_node_count,
2238            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
2239            node_capacity: self.nodes.capacity(),
2240            total_links,
2241            live_links: live_link_count,
2242            dead_links: total_links.saturating_sub(live_link_count),
2243            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
2244            total_extras: self.extras.len(),
2245            live_extras: live_extra_count,
2246            dead_extras: self.extras.len().saturating_sub(live_extra_count),
2247            extra_capacity: self.extras.capacity(),
2248        }
2249    }
2250}
2251
2252fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
2253    if storage.capacity() > max_retained_capacity {
2254        *storage = Vec::new();
2255    } else {
2256        storage.clear();
2257    }
2258}
2259
2260const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2261    match node {
2262        ArenaRecognizedNode::Token { .. } => 0,
2263        ArenaRecognizedNode::ErrorToken { .. } => 1,
2264        ArenaRecognizedNode::MissingToken { .. } => 2,
2265        ArenaRecognizedNode::Rule { .. } => 3,
2266        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2267    }
2268}
2269
2270struct NodeSeqIter<'a> {
2271    arena: &'a RecognitionArena,
2272    cursor: NodeSeqId,
2273}
2274
2275impl Iterator for NodeSeqIter<'_> {
2276    type Item = RecognizedNodeId;
2277
2278    fn next(&mut self) -> Option<Self::Item> {
2279        let link = self.arena.link(self.cursor)?;
2280        self.cursor = link.tail;
2281        Some(link.head)
2282    }
2283}
2284
2285struct DiagnosticSeqIter<'a> {
2286    arena: &'a RecognitionArena,
2287    cursor: DiagnosticSeqId,
2288}
2289
2290impl<'a> Iterator for DiagnosticSeqIter<'a> {
2291    type Item = &'a ParserDiagnostic;
2292
2293    fn next(&mut self) -> Option<Self::Item> {
2294        let link = self.arena.diagnostic_link(self.cursor)?;
2295        self.cursor = link.tail;
2296        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2297            unreachable!("diagnostic link must reference diagnostic extra");
2298        };
2299        Some(diagnostic)
2300    }
2301}
2302
2303#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2304struct ParserDiagnostic {
2305    line: usize,
2306    column: usize,
2307    message: String,
2308    /// Token the diagnostic is anchored to, resolved to a view when the
2309    /// diagnostic is dispatched to error listeners. `None` when no token
2310    /// exists (synthetic positions, lexer-originated messages).
2311    offending: Option<TokenId>,
2312}
2313
2314#[derive(Clone, Debug, Default, Eq, PartialEq)]
2315struct ExpectedTokens {
2316    index: Option<usize>,
2317    symbols: BTreeSet<i32>,
2318    no_viable: Option<NoViableAlternative>,
2319}
2320
2321#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2322struct NoViableAlternative {
2323    start_index: usize,
2324    error_index: usize,
2325}
2326
2327impl ExpectedTokens {
2328    /// Records the expected symbols for the farthest token index reached by any
2329    /// failed ATN path.
2330    fn record_transition(
2331        &mut self,
2332        index: usize,
2333        transition: ParserTransition<'_>,
2334        max_token_type: i32,
2335    ) {
2336        let symbols = transition_expected_symbols(transition, max_token_type);
2337        match self.index {
2338            Some(current) if index < current => {}
2339            Some(current) if index == current => self.symbols.extend(symbols),
2340            _ => {
2341                self.index = Some(index);
2342                self.symbols = symbols;
2343            }
2344        }
2345    }
2346
2347    /// Records an ambiguous decision that failed after consuming a shared
2348    /// prefix, which ANTLR reports as `no viable alternative`.
2349    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2350        match self.no_viable {
2351            Some(current) if error_index < current.error_index => {}
2352            _ => {
2353                self.no_viable = Some(NoViableAlternative {
2354                    start_index,
2355                    error_index,
2356                });
2357            }
2358        }
2359    }
2360}
2361
2362/// Compact token-type set for parser-internal FIRST/lookahead caches.
2363///
2364/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2365/// but the hot recognizer path mostly needs `contains` and set union over
2366/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2367/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2368#[derive(Clone, Debug, Default, Eq, PartialEq)]
2369struct TokenBitSet {
2370    words: Vec<u64>,
2371}
2372
2373impl TokenBitSet {
2374    fn insert(&mut self, symbol: i32) {
2375        let Some(slot) = token_bit_slot(symbol) else {
2376            return;
2377        };
2378        let word = slot / u64::BITS as usize;
2379        if word >= self.words.len() {
2380            self.words.resize(word + 1, 0);
2381        }
2382        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2383    }
2384
2385    fn extend_range(&mut self, start: i32, stop: i32) {
2386        let (start, stop) = if start <= stop {
2387            (start, stop)
2388        } else {
2389            (stop, start)
2390        };
2391        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2392            self.insert(TOKEN_EOF);
2393        }
2394        let positive_start = start.max(1);
2395        if positive_start > stop {
2396            return;
2397        }
2398        let Some(start_slot) = token_bit_slot(positive_start) else {
2399            return;
2400        };
2401        let Some(stop_slot) = token_bit_slot(stop) else {
2402            return;
2403        };
2404        self.extend_slot_range(start_slot, stop_slot);
2405    }
2406
2407    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2408        if start_slot > stop_slot {
2409            return;
2410        }
2411        let start_word = start_slot / u64::BITS as usize;
2412        let stop_word = stop_slot / u64::BITS as usize;
2413        if stop_word >= self.words.len() {
2414            self.words.resize(stop_word + 1, 0);
2415        }
2416        let start_offset = start_slot % u64::BITS as usize;
2417        let stop_offset = stop_slot % u64::BITS as usize;
2418        if start_word == stop_word {
2419            self.words[start_word] |=
2420                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2421            return;
2422        }
2423        self.words[start_word] |= !0_u64 << start_offset;
2424        for word in &mut self.words[(start_word + 1)..stop_word] {
2425            *word = !0_u64;
2426        }
2427        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2428    }
2429
2430    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2431        for symbol in symbols {
2432            self.insert(symbol);
2433        }
2434    }
2435
2436    fn extend_from(&mut self, other: &Self) {
2437        if other.words.len() > self.words.len() {
2438            self.words.resize(other.words.len(), 0);
2439        }
2440        for (left, right) in self.words.iter_mut().zip(&other.words) {
2441            *left |= *right;
2442        }
2443    }
2444
2445    fn contains(&self, symbol: i32) -> bool {
2446        let Some(slot) = token_bit_slot(symbol) else {
2447            return false;
2448        };
2449        let word = slot / u64::BITS as usize;
2450        self.words
2451            .get(word)
2452            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2453    }
2454
2455    fn is_empty(&self) -> bool {
2456        self.words.iter().all(|word| *word == 0)
2457    }
2458
2459    fn symbols(&self) -> impl Iterator<Item = i32> + '_ {
2460        self.words
2461            .iter()
2462            .copied()
2463            .enumerate()
2464            .flat_map(|(word_index, mut bits)| {
2465                std::iter::from_fn(move || {
2466                    while bits != 0 {
2467                        let bit = bits.trailing_zeros() as usize;
2468                        bits &= bits - 1;
2469                        if let Some(symbol) =
2470                            token_bit_symbol(word_index * u64::BITS as usize + bit)
2471                        {
2472                            return Some(symbol);
2473                        }
2474                    }
2475                    None
2476                })
2477            })
2478    }
2479
2480    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2481        target.extend(self.symbols());
2482    }
2483
2484    fn to_btree_set(&self) -> BTreeSet<i32> {
2485        let mut out = BTreeSet::new();
2486        self.extend_btree_set(&mut out);
2487        out
2488    }
2489}
2490
2491fn token_bit_slot(symbol: i32) -> Option<usize> {
2492    if symbol == TOKEN_EOF {
2493        Some(0)
2494    } else if symbol > 0 {
2495        usize::try_from(symbol).ok()
2496    } else {
2497        None
2498    }
2499}
2500
2501fn token_bit_symbol(slot: usize) -> Option<i32> {
2502    if slot == 0 {
2503        Some(TOKEN_EOF)
2504    } else {
2505        i32::try_from(slot).ok()
2506    }
2507}
2508
2509/// Converts one consuming transition into the token types that would satisfy it
2510/// for diagnostic reporting.
2511fn transition_expected_symbols(
2512    transition: ParserTransition<'_>,
2513    max_token_type: i32,
2514) -> BTreeSet<i32> {
2515    let mut symbols = BTreeSet::new();
2516    match &transition.data() {
2517        Transition::Atom { label, .. } => {
2518            symbols.insert(*label);
2519        }
2520        Transition::Range { start, stop, .. } => {
2521            symbols.extend(*start..=*stop);
2522        }
2523        Transition::Set { set, .. } => {
2524            for (start, stop) in set.ranges() {
2525                symbols.extend(start..=stop);
2526            }
2527        }
2528        Transition::NotSet { set, .. } => {
2529            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2530        }
2531        Transition::Wildcard { .. } => {
2532            symbols.extend(1..=max_token_type);
2533        }
2534        Transition::Epsilon { .. }
2535        | Transition::Rule { .. }
2536        | Transition::Predicate { .. }
2537        | Transition::Action { .. }
2538        | Transition::Precedence { .. } => {}
2539    }
2540    symbols
2541}
2542
2543fn transition_expected_token_set(
2544    transition: ParserTransition<'_>,
2545    max_token_type: i32,
2546) -> TokenBitSet {
2547    let mut symbols = TokenBitSet::default();
2548    match &transition.data() {
2549        Transition::Atom { label, .. } => {
2550            symbols.insert(*label);
2551        }
2552        Transition::Range { start, stop, .. } => {
2553            symbols.extend_range(*start, *stop);
2554        }
2555        Transition::Set { set, .. } => {
2556            for (start, stop) in set.ranges() {
2557                symbols.extend_range(start, stop);
2558            }
2559        }
2560        Transition::NotSet { set, .. } => {
2561            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2562        }
2563        Transition::Wildcard { .. } => {
2564            symbols.extend_range(1, max_token_type);
2565        }
2566        Transition::Epsilon { .. }
2567        | Transition::Rule { .. }
2568        | Transition::Predicate { .. }
2569        | Transition::Action { .. }
2570        | Transition::Precedence { .. } => {}
2571    }
2572    symbols
2573}
2574
2575/// Returns the consuming-token expectations reachable from an ATN state through
2576/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2577/// and loop exits report the same expectation set ANTLR users see.
2578fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2579    let mut symbols = BTreeSet::new();
2580    let mut stack = vec![state_number];
2581    let mut visited = BTreeSet::new();
2582    while let Some(current) = stack.pop() {
2583        if !visited.insert(current) {
2584            continue;
2585        }
2586        let Some(state) = atn.state(current) else {
2587            continue;
2588        };
2589        for transition in &state.transitions() {
2590            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2591            if transition_symbols.is_empty() {
2592                if transition.is_epsilon() {
2593                    stack.push(transition.target());
2594                }
2595            } else {
2596                symbols.extend(transition_symbols);
2597            }
2598        }
2599    }
2600    symbols
2601}
2602
2603fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2604    let mut symbols = TokenBitSet::default();
2605    let mut stack = vec![state_number];
2606    let mut visited = BTreeSet::new();
2607    while let Some(current) = stack.pop() {
2608        if !visited.insert(current) {
2609            continue;
2610        }
2611        let Some(state) = atn.state(current) else {
2612            continue;
2613        };
2614        for transition in &state.transitions() {
2615            let transition_symbols =
2616                transition_expected_token_set(transition, atn.max_token_type());
2617            if transition_symbols.is_empty() {
2618                if transition.is_epsilon() {
2619                    stack.push(transition.target());
2620                }
2621            } else {
2622                symbols.extend_from(&transition_symbols);
2623            }
2624        }
2625    }
2626    symbols
2627}
2628
2629fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2630    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2631        return false;
2632    };
2633    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2634        return false;
2635    };
2636    epsilon_reaches_state(atn, state_number, stop_state)
2637}
2638
2639fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2640    let mut stack = vec![start];
2641    let mut visited = BTreeSet::new();
2642    while let Some(current) = stack.pop() {
2643        if current == target {
2644            return true;
2645        }
2646        if !visited.insert(current) {
2647            continue;
2648        }
2649        let Some(state) = atn.state(current) else {
2650            continue;
2651        };
2652        stack.extend(
2653            state
2654                .transitions()
2655                .iter()
2656                .filter(|transition| transition.is_epsilon())
2657                .map(ParserTransition::target),
2658        );
2659    }
2660    false
2661}
2662
2663/// FIRST set for a rule entry plus whether the rule is nullable.
2664///
2665/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2666/// a consuming transition or reaches the rule's stop state. Used by the fast
2667/// recognizer to skip rule alternatives whose first-consumed token cannot
2668/// possibly match the current lookahead.
2669#[derive(Clone, Debug, Default, Eq, PartialEq)]
2670struct FirstSet {
2671    symbols: TokenBitSet,
2672    nullable: bool,
2673}
2674
2675/// Per-parser cache of FIRST sets computed during recognition. The fast path
2676/// consults this on every speculative `Transition::Rule` encounter, so the
2677/// computation must amortize across all of those calls — the FIRST set is a
2678/// pure function of the ATN, not of the input position. Cached entries are
2679/// shared via `Rc` so the recognizer never deep-copies the underlying
2680/// `BTreeSet<i32>`.
2681type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2682
2683// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2684// and decision-lookahead entries are purely functions of the grammar's
2685// ATN, so caching across parses lets repeated parsing of the same grammar
2686// (the common case for a CLI tool or language server) avoid redoing the
2687// closure work. Generated parsers hand us a `&'static Atn` whose address
2688// is stable, which is what we hash on.
2689type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2690
2691#[derive(Debug, Default)]
2692struct LeftRecursiveOperatorLookahead {
2693    /// Operator alts whose token-prefix is fully matched by this one symbol
2694    /// (then only epsilons/actions remain before the recursive RHS call).
2695    /// Safe for one-token loop-enter fast path.
2696    single_token: TokenBitSet,
2697    /// Operator alts that start with this symbol but still require more tokens
2698    /// before the operand. Must not force enter from one-token lookahead when a
2699    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2700    /// has to weigh the exit alt as well.
2701    multi_token_prefix: TokenBitSet,
2702    predicate_dependent: TokenBitSet,
2703}
2704
2705#[derive(Default)]
2706struct SharedAtnCache {
2707    first_set: FirstSetCache,
2708    decision_lookahead: DecisionLookaheadCache,
2709    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2710    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2711    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2712    rule_stop_reach: FxHashMap<usize, bool>,
2713    observable_action_transitions: Option<bool>,
2714    predicate_transitions: Option<bool>,
2715}
2716
2717thread_local! {
2718    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2719        RefCell::new(FxHashMap::default());
2720}
2721
2722/// Compound key for `SHARED_ATN_CACHES`.
2723///
2724/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2725/// pointer identifies one grammar for the program's lifetime. For the
2726/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2727/// the secondary fields below catch the pointer collision: a new grammar
2728/// would need to match all of `(states ptr, states len, max_token_type)` to
2729/// be mistaken for the dropped one. That combination changing under us
2730/// without a rebuild is implausible enough to treat as a bug; bundling them
2731/// into the key is otherwise a few extra bytes per lookup.
2732#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2733struct SharedAtnCacheKey {
2734    atn: usize,
2735    states: usize,
2736    state_count: usize,
2737    max_token_type: i32,
2738}
2739
2740impl SharedAtnCacheKey {
2741    fn for_atn(atn: &Atn) -> Self {
2742        let (states, state_count) = atn.storage_identity();
2743        Self {
2744            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2745            states,
2746            state_count,
2747            max_token_type: atn.max_token_type(),
2748        }
2749    }
2750}
2751
2752fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2753    SHARED_ATN_CACHES.with(|cell| {
2754        let key = SharedAtnCacheKey::for_atn(atn);
2755        let mut map = cell.borrow_mut();
2756        let cache = map.entry(key).or_default();
2757        f(&mut cache.first_set)
2758    })
2759}
2760
2761fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2762    SHARED_ATN_CACHES.with(|cell| {
2763        let key = SharedAtnCacheKey::for_atn(atn);
2764        let mut map = cell.borrow_mut();
2765        let cache = map.entry(key).or_default();
2766        f(cache)
2767    })
2768}
2769
2770/// Per-decision-state cached look-1 sets for each outgoing transition.
2771///
2772/// At a multi-alternative state, the recognizer would otherwise speculatively
2773/// walk every alternative even when only one can possibly accept the current
2774/// lookahead. Caching the look-1 set per transition lets us prune the
2775/// non-viable transitions before recursing — the same SLL prediction trick
2776/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2777/// filter rather than a full DFA.
2778#[derive(Debug, Default)]
2779struct DecisionLookahead {
2780    transitions: Vec<TransitionLookSet>,
2781}
2782
2783/// Look-1 information for one outgoing transition.
2784///
2785/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2786/// can reach the rule stop without consuming a token (e.g. an empty alt).
2787/// Nullable transitions cannot be pruned: they may still be the right path
2788/// when the lookahead consumes nothing further inside the current rule.
2789#[derive(Clone, Debug, Default)]
2790struct TransitionLookSet {
2791    symbols: TokenBitSet,
2792    nullable: bool,
2793}
2794
2795/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2796/// rarely-touched fields keeps the recursive helpers below the function-arity
2797/// lint and lets every nested call thread the same cache and cycle guards.
2798struct FirstSetCtx<'a> {
2799    cache: &'a mut FirstSetCache,
2800    in_progress: BTreeSet<(usize, usize)>,
2801    hit_cycle: bool,
2802}
2803
2804/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2805/// shared cache and tolerating recursive nullable rule chains. Mutually
2806/// recursive rules cannot stack-overflow because callers in flight are tracked
2807/// in `ctx.in_progress`; revisits return without recursing, and the partial
2808/// result is cached only when no cycle was detected during its computation.
2809///
2810/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2811/// rule-call probes only pay a reference bump.
2812fn rule_first_set(
2813    atn: &Atn,
2814    target: usize,
2815    rule_stop_state: usize,
2816    cache: &mut FirstSetCache,
2817) -> Rc<FirstSet> {
2818    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2819        return Rc::clone(cached);
2820    }
2821    let mut ctx = FirstSetCtx {
2822        cache,
2823        in_progress: BTreeSet::new(),
2824        hit_cycle: false,
2825    };
2826    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2827}
2828
2829fn rule_first_set_cached(
2830    atn: &Atn,
2831    target: usize,
2832    rule_stop_state: usize,
2833    ctx: &mut FirstSetCtx<'_>,
2834) -> Rc<FirstSet> {
2835    let key = (target, rule_stop_state);
2836    if let Some(cached) = ctx.cache.get(&key) {
2837        return Rc::clone(cached);
2838    }
2839    if !ctx.in_progress.insert(key) {
2840        // Cycle: a caller above is already computing this entry. Return an
2841        // empty FIRST set; that caller's traversal supplies the contributions
2842        // from the rule's other alternatives.
2843        return Rc::new(FirstSet::default());
2844    }
2845    let saved_hit_cycle = ctx.hit_cycle;
2846    ctx.hit_cycle = false;
2847    let mut first = FirstSet::default();
2848    let mut visited = BTreeSet::new();
2849    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2850    ctx.in_progress.remove(&key);
2851    let entry = Rc::new(first);
2852    if !ctx.hit_cycle {
2853        ctx.cache.insert(key, Rc::clone(&entry));
2854    }
2855    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2856    entry
2857}
2858
2859/// Returns the look-1 set for traversing `transition` while still inside the
2860/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2861/// prunes transitions whose look-1 cannot accept the current lookahead.
2862fn transition_first_set(
2863    atn: &Atn,
2864    transition: ParserTransition<'_>,
2865    rule_stop_state: usize,
2866    cache: &mut FirstSetCache,
2867) -> TransitionLookSet {
2868    match &transition.data() {
2869        Transition::Atom { label, .. } => {
2870            let mut symbols = TokenBitSet::default();
2871            symbols.insert(*label);
2872            TransitionLookSet {
2873                symbols,
2874                nullable: false,
2875            }
2876        }
2877        Transition::Range { start, stop, .. } => {
2878            let mut symbols = TokenBitSet::default();
2879            symbols.extend_range(*start, *stop);
2880            TransitionLookSet {
2881                symbols,
2882                nullable: false,
2883            }
2884        }
2885        Transition::Set { set, .. } => {
2886            let mut symbols = TokenBitSet::default();
2887            for (start, stop) in set.ranges() {
2888                symbols.extend_range(start, stop);
2889            }
2890            TransitionLookSet {
2891                symbols,
2892                nullable: false,
2893            }
2894        }
2895        Transition::NotSet { set, .. } => {
2896            let max = atn.max_token_type();
2897            let mut symbols = TokenBitSet::default();
2898            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2899            TransitionLookSet {
2900                symbols,
2901                nullable: false,
2902            }
2903        }
2904        Transition::Wildcard { .. } => {
2905            let mut symbols = TokenBitSet::default();
2906            symbols.extend_range(1, atn.max_token_type());
2907            TransitionLookSet {
2908                symbols,
2909                nullable: false,
2910            }
2911        }
2912        Transition::Epsilon { target }
2913        | Transition::Action { target, .. }
2914        | Transition::Predicate { target, .. }
2915        | Transition::Precedence { target, .. } => {
2916            // Walk the closure starting at `target` until a consuming transition
2917            // is reached or the rule stop state is hit.
2918            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2919            TransitionLookSet {
2920                symbols: first.symbols.clone(),
2921                nullable: first.nullable,
2922            }
2923        }
2924        Transition::Rule {
2925            target,
2926            rule_index,
2927            follow_state,
2928            ..
2929        } => {
2930            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2931                return TransitionLookSet::default();
2932            };
2933            let child = rule_first_set(atn, *target, child_stop, cache);
2934            let mut symbols = child.symbols.clone();
2935            let nullable = if child.nullable {
2936                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2937                symbols.extend_from(&follow.symbols);
2938                follow.nullable
2939            } else {
2940                false
2941            };
2942            TransitionLookSet { symbols, nullable }
2943        }
2944    }
2945}
2946
2947/// Reports whether `transition` can be pruned at a multi-alt state because
2948/// its cached look-1 cannot accept the current lookahead.
2949///
2950/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2951/// Rule/Precedence) so consuming transitions still reach the
2952/// `matches`+recovery path that surfaces single-token deletion / insertion
2953/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2954/// transition is pruned, its FIRST set is folded into `expected` so failed
2955/// parses produce the same `mismatched input ... expecting ...` diagnostic
2956/// the no-prefilter baseline would emit.
2957/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2958/// one transition's FIRST set and no transition is nullable. Used as an
2959/// LL(1) commit point: when prediction is unambiguous from the lookahead
2960/// alone, the recursive recognizer can skip every other alt without paying
2961/// for the per-transition filter probe.
2962///
2963/// `None` signals the caller to fall back to per-transition lookahead
2964/// filtering. Returning `Some` for an alt whose transition cannot actually
2965/// match would prune the only viable parse path; this is why we require
2966/// strict disjointness *and* no nullable transitions in the decision.
2967fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2968    let mut chosen: Option<usize> = None;
2969    for (index, transition) in entry.transitions.iter().enumerate() {
2970        if transition.nullable {
2971            return None;
2972        }
2973        if transition.symbols.contains(symbol) {
2974            if chosen.is_some() {
2975                return None;
2976            }
2977            chosen = Some(index);
2978        }
2979    }
2980    chosen
2981}
2982
2983/// Returns the unique greedy alt index (0-based) selected by the current
2984/// lookahead.
2985///
2986/// The shortcut is intentionally conservative around nullable exits. If the
2987/// current symbol can start a consuming alternative and an empty alternative is
2988/// also present, one-token lookahead is not enough to know whether the symbol
2989/// belongs to the current construct or to its caller's follow set. `None`
2990/// signals the caller to fall back to adaptive prediction.
2991fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2992    let mut matching_non_nullable_alt = None;
2993    let mut nullable_alt = None;
2994    for (index, transition) in entry.transitions.iter().enumerate() {
2995        if transition.nullable {
2996            if nullable_alt.is_some() {
2997                return None;
2998            }
2999            nullable_alt = Some(index);
3000        }
3001        if transition.symbols.contains(symbol) {
3002            if transition.nullable {
3003                continue;
3004            }
3005            if matching_non_nullable_alt.is_some() {
3006                return None;
3007            }
3008            matching_non_nullable_alt = Some(index);
3009        }
3010    }
3011    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
3012        return None;
3013    }
3014    if non_greedy {
3015        nullable_alt.or(matching_non_nullable_alt)
3016    } else {
3017        matching_non_nullable_alt.or(nullable_alt)
3018    }
3019}
3020
3021fn should_skip_via_lookahead(
3022    transition_kind: ParserTransitionKind,
3023    transition_index: usize,
3024    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
3025    index: usize,
3026    record_expected: bool,
3027    expected: &mut ExpectedTokens,
3028) -> bool {
3029    let prune_non_consuming = matches!(
3030        transition_kind,
3031        ParserTransitionKind::Epsilon
3032            | ParserTransitionKind::Action
3033            | ParserTransitionKind::Predicate
3034            | ParserTransitionKind::Rule
3035            | ParserTransitionKind::Precedence
3036    );
3037    if !prune_non_consuming {
3038        return false;
3039    }
3040    let Some((symbol, entry)) = lookahead_filter else {
3041        return false;
3042    };
3043    let Some(set) = entry.transitions.get(transition_index) else {
3044        return false;
3045    };
3046    if set.symbols.contains(*symbol) || set.nullable {
3047        return false;
3048    }
3049    if record_expected && !set.symbols.is_empty() {
3050        record_pruned_transition_expected(set, index, expected);
3051    }
3052    true
3053}
3054
3055fn should_skip_rule_via_first_set(
3056    first: &FirstSet,
3057    symbol: i32,
3058    record_expected: bool,
3059    index: usize,
3060    expected: &mut ExpectedTokens,
3061) -> bool {
3062    if first.nullable || first.symbols.contains(symbol) {
3063        return false;
3064    }
3065    if record_expected && !first.symbols.is_empty() {
3066        record_token_bit_expected(&first.symbols, index, expected);
3067    }
3068    true
3069}
3070
3071fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
3072    match expected.index {
3073        Some(current) if index < current => {}
3074        Some(current) if index == current => {
3075            symbols.extend_btree_set(&mut expected.symbols);
3076        }
3077        _ => {
3078            expected.index = Some(index);
3079            expected.symbols = symbols.to_btree_set();
3080        }
3081    }
3082}
3083
3084/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
3085fn record_pruned_transition_expected(
3086    set: &TransitionLookSet,
3087    index: usize,
3088    expected: &mut ExpectedTokens,
3089) {
3090    match expected.index {
3091        Some(current) if index < current => {}
3092        Some(current) if index == current => {
3093            set.symbols.extend_btree_set(&mut expected.symbols);
3094        }
3095        _ => {
3096            expected.index = Some(index);
3097            expected.symbols = set.symbols.to_btree_set();
3098        }
3099    }
3100}
3101
3102fn rule_first_set_inner(
3103    atn: &Atn,
3104    state_number: usize,
3105    rule_stop_state: usize,
3106    ctx: &mut FirstSetCtx<'_>,
3107    visited: &mut BTreeSet<usize>,
3108    first: &mut FirstSet,
3109) {
3110    if !visited.insert(state_number) {
3111        return;
3112    }
3113    if state_number == rule_stop_state {
3114        first.nullable = true;
3115        return;
3116    }
3117    let Some(state) = atn.state(state_number) else {
3118        return;
3119    };
3120    for transition in &state.transitions() {
3121        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3122        if !transition_symbols.is_empty() {
3123            first.symbols.extend_iter(transition_symbols);
3124            continue;
3125        }
3126        match &transition.data() {
3127            Transition::Epsilon { target }
3128            | Transition::Action { target, .. }
3129            | Transition::Predicate { target, .. }
3130            | Transition::Precedence { target, .. } => {
3131                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
3132            }
3133            Transition::Rule {
3134                target,
3135                rule_index,
3136                follow_state,
3137                ..
3138            } => {
3139                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3140                    continue;
3141                };
3142                let child_key = (*target, child_stop);
3143                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
3144                    ctx.hit_cycle = true;
3145                }
3146                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
3147                first.symbols.extend_from(&child.symbols);
3148                if child.nullable {
3149                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
3150                }
3151            }
3152            Transition::Atom { .. }
3153            | Transition::Range { .. }
3154            | Transition::Set { .. }
3155            | Transition::NotSet { .. }
3156            | Transition::Wildcard { .. } => {}
3157        }
3158    }
3159}
3160
3161/// Returns token types that can resume parsing from `state_number` after a
3162/// failed child rule, following rule calls as well as epsilon transitions.
3163fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
3164    let mut symbols = BTreeSet::new();
3165    state_sync_symbols_inner(
3166        atn,
3167        state_number,
3168        stop_state,
3169        &mut BTreeSet::new(),
3170        &mut symbols,
3171    );
3172    symbols
3173}
3174
3175/// Walks epsilon-like continuations from a parent follow state until it finds
3176/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
3177fn state_sync_symbols_inner(
3178    atn: &Atn,
3179    state_number: usize,
3180    stop_state: usize,
3181    visited: &mut BTreeSet<usize>,
3182    symbols: &mut BTreeSet<i32>,
3183) {
3184    if !visited.insert(state_number) {
3185        return;
3186    }
3187    if state_number == stop_state {
3188        symbols.insert(TOKEN_EOF);
3189        return;
3190    }
3191    let Some(state) = atn.state(state_number) else {
3192        return;
3193    };
3194    for transition in &state.transitions() {
3195        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3196        if transition_symbols.is_empty() {
3197            match &transition.data() {
3198                Transition::Rule { target, .. }
3199                | Transition::Epsilon { target }
3200                | Transition::Action { target, .. }
3201                | Transition::Predicate { target, .. }
3202                | Transition::Precedence { target, .. } => {
3203                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
3204                }
3205                Transition::Atom { .. }
3206                | Transition::Range { .. }
3207                | Transition::Set { .. }
3208                | Transition::NotSet { .. }
3209                | Transition::Wildcard { .. } => {}
3210            }
3211        } else {
3212            symbols.extend(transition_symbols);
3213        }
3214    }
3215}
3216
3217#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3218struct OperatorSymbolReachability {
3219    /// One token completes an unconditional operator token-prefix.
3220    single_token: bool,
3221    /// An unconditional operator path requires more tokens before its operand.
3222    multi_token: bool,
3223    /// At least one matching operator path depends on a semantic predicate.
3224    predicate_dependent: bool,
3225}
3226
3227impl OperatorSymbolReachability {
3228    const ADAPTIVE_FALLBACK: Self = Self {
3229        single_token: false,
3230        multi_token: false,
3231        predicate_dependent: true,
3232    };
3233
3234    const fn single_token(predicate_dependent: bool) -> Self {
3235        if predicate_dependent {
3236            Self {
3237                single_token: false,
3238                multi_token: false,
3239                predicate_dependent: true,
3240            }
3241        } else {
3242            Self {
3243                single_token: true,
3244                multi_token: false,
3245                predicate_dependent: false,
3246            }
3247        }
3248    }
3249
3250    const fn multi_token(predicate_dependent: bool) -> Self {
3251        if predicate_dependent {
3252            Self {
3253                single_token: false,
3254                multi_token: false,
3255                predicate_dependent: true,
3256            }
3257        } else {
3258            Self {
3259                single_token: false,
3260                multi_token: true,
3261                predicate_dependent: false,
3262            }
3263        }
3264    }
3265
3266    const fn union(self, other: Self) -> Self {
3267        Self {
3268            single_token: self.single_token || other.single_token,
3269            multi_token: self.multi_token || other.multi_token,
3270            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3271        }
3272    }
3273}
3274
3275#[derive(Clone, Copy)]
3276struct OperatorReachabilityRequest {
3277    symbol: i32,
3278    precedence: i32,
3279    predicate_dependent: bool,
3280    operator_rule_index: usize,
3281}
3282
3283#[derive(Clone, Copy, Debug)]
3284struct OperatorRuleContinuation {
3285    stop_state: usize,
3286    follow_state: usize,
3287    return_precedence: i32,
3288}
3289
3290struct NullablePrecedenceCtx {
3291    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3292    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3293    hit_cycle: bool,
3294}
3295
3296fn state_is_nullable_with_precedence(
3297    atn: &Atn,
3298    state_number: usize,
3299    stop_state_number: usize,
3300    precedence: i32,
3301    allow_predicates: bool,
3302    ctx: &mut NullablePrecedenceCtx,
3303) -> bool {
3304    let saved_hit_cycle = ctx.hit_cycle;
3305    ctx.hit_cycle = false;
3306    let nullable = state_is_nullable_with_precedence_cached(
3307        atn,
3308        state_number,
3309        stop_state_number,
3310        precedence,
3311        allow_predicates,
3312        ctx,
3313    );
3314    ctx.hit_cycle = saved_hit_cycle;
3315    nullable
3316}
3317
3318fn state_is_nullable_with_precedence_cached(
3319    atn: &Atn,
3320    state_number: usize,
3321    stop_state_number: usize,
3322    precedence: i32,
3323    allow_predicates: bool,
3324    ctx: &mut NullablePrecedenceCtx,
3325) -> bool {
3326    if state_number == stop_state_number {
3327        return true;
3328    }
3329    let key = (
3330        state_number,
3331        stop_state_number,
3332        precedence,
3333        allow_predicates,
3334    );
3335    if let Some(cached) = ctx.cache.get(&key) {
3336        return *cached;
3337    }
3338    if !ctx.in_progress.insert(key) {
3339        ctx.hit_cycle = true;
3340        return false;
3341    }
3342    let saved_hit_cycle = ctx.hit_cycle;
3343    ctx.hit_cycle = false;
3344    let nullable = atn.state(state_number).is_some_and(|state| {
3345        state
3346            .transitions()
3347            .iter()
3348            .any(|transition| match &transition.data() {
3349                Transition::Rule {
3350                    target,
3351                    rule_index,
3352                    follow_state,
3353                    precedence: rule_precedence,
3354                } => {
3355                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3356                        return false;
3357                    };
3358                    state_is_nullable_with_precedence_cached(
3359                        atn,
3360                        *target,
3361                        child_stop,
3362                        *rule_precedence,
3363                        allow_predicates,
3364                        ctx,
3365                    ) && state_is_nullable_with_precedence_cached(
3366                        atn,
3367                        *follow_state,
3368                        stop_state_number,
3369                        precedence,
3370                        allow_predicates,
3371                        ctx,
3372                    )
3373                }
3374                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3375                    state_is_nullable_with_precedence_cached(
3376                        atn,
3377                        *target,
3378                        stop_state_number,
3379                        precedence,
3380                        allow_predicates,
3381                        ctx,
3382                    )
3383                }
3384                Transition::Predicate { target, .. } if allow_predicates => {
3385                    state_is_nullable_with_precedence_cached(
3386                        atn,
3387                        *target,
3388                        stop_state_number,
3389                        precedence,
3390                        allow_predicates,
3391                        ctx,
3392                    )
3393                }
3394                Transition::Precedence {
3395                    target,
3396                    precedence: transition_precedence,
3397                } if *transition_precedence >= precedence => {
3398                    state_is_nullable_with_precedence_cached(
3399                        atn,
3400                        *target,
3401                        stop_state_number,
3402                        precedence,
3403                        allow_predicates,
3404                        ctx,
3405                    )
3406                }
3407                Transition::Atom { .. }
3408                | Transition::Range { .. }
3409                | Transition::Set { .. }
3410                | Transition::NotSet { .. }
3411                | Transition::Wildcard { .. }
3412                | Transition::Predicate { .. }
3413                | Transition::Precedence { .. } => false,
3414            })
3415    });
3416    ctx.in_progress.remove(&key);
3417    if !ctx.hit_cycle {
3418        ctx.cache.insert(key, nullable);
3419    }
3420    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3421    nullable
3422}
3423
3424/// Classifies what remains after the operator's first token is matched.
3425fn state_operator_token_prefix_reachability(
3426    atn: &Atn,
3427    state_number: usize,
3428    request: OperatorReachabilityRequest,
3429    continuations: &[OperatorRuleContinuation],
3430    visited: &mut BTreeSet<(usize, i32, bool)>,
3431) -> OperatorSymbolReachability {
3432    let key = (
3433        state_number,
3434        request.precedence,
3435        request.predicate_dependent,
3436    );
3437    if !visited.insert(key) {
3438        // Recursive helper rules can grow the return stack without consuming
3439        // input. Delegate cycles to adaptive prediction instead of forcing a
3440        // potentially incomplete one-token answer.
3441        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3442    }
3443    if let Some((continuation, remaining)) = continuations.split_last()
3444        && state_number == continuation.stop_state
3445    {
3446        let result = state_operator_token_prefix_reachability(
3447            atn,
3448            continuation.follow_state,
3449            OperatorReachabilityRequest {
3450                precedence: continuation.return_precedence,
3451                ..request
3452            },
3453            remaining,
3454            visited,
3455        );
3456        visited.remove(&key);
3457        return result;
3458    }
3459    let Some(state) = atn.state(state_number) else {
3460        visited.remove(&key);
3461        return OperatorSymbolReachability::default();
3462    };
3463    let completes_operator = match state.kind() {
3464        AtnStateKind::RuleStop => continuations.is_empty(),
3465        AtnStateKind::StarLoopBack
3466        | AtnStateKind::StarLoopEntry
3467        | AtnStateKind::PlusLoopBack
3468        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3469        _ => false,
3470    };
3471    if completes_operator {
3472        visited.remove(&key);
3473        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3474    }
3475    let mut reachability = OperatorSymbolReachability::default();
3476    for transition in &state.transitions() {
3477        let transition_reachability = match &transition.data() {
3478            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3479                OperatorSymbolReachability::single_token(request.predicate_dependent)
3480            }
3481            Transition::Rule {
3482                target,
3483                rule_index,
3484                follow_state,
3485                precedence: rule_precedence,
3486            } => {
3487                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3488                    continue;
3489                };
3490                let mut nested = continuations.to_vec();
3491                nested.push(OperatorRuleContinuation {
3492                    stop_state: child_stop,
3493                    follow_state: *follow_state,
3494                    return_precedence: request.precedence,
3495                });
3496                state_operator_token_prefix_reachability(
3497                    atn,
3498                    *target,
3499                    OperatorReachabilityRequest {
3500                        precedence: *rule_precedence,
3501                        ..request
3502                    },
3503                    &nested,
3504                    visited,
3505                )
3506            }
3507            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3508                state_operator_token_prefix_reachability(
3509                    atn,
3510                    *target,
3511                    request,
3512                    continuations,
3513                    visited,
3514                )
3515            }
3516            Transition::Precedence {
3517                target,
3518                precedence: transition_precedence,
3519            } => {
3520                if *transition_precedence < request.precedence {
3521                    OperatorSymbolReachability::default()
3522                } else {
3523                    state_operator_token_prefix_reachability(
3524                        atn,
3525                        *target,
3526                        request,
3527                        continuations,
3528                        visited,
3529                    )
3530                }
3531            }
3532            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3533                atn,
3534                *target,
3535                OperatorReachabilityRequest {
3536                    predicate_dependent: true,
3537                    ..request
3538                },
3539                continuations,
3540                visited,
3541            ),
3542            Transition::Atom { .. }
3543            | Transition::Range { .. }
3544            | Transition::Set { .. }
3545            | Transition::NotSet { .. }
3546            | Transition::Wildcard { .. } => {
3547                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3548            }
3549        };
3550        reachability = reachability.union(transition_reachability);
3551    }
3552    visited.remove(&key);
3553    reachability
3554}
3555
3556fn state_can_reach_symbol_with_precedence(
3557    atn: &Atn,
3558    state_number: usize,
3559    request: OperatorReachabilityRequest,
3560    nullable_ctx: &mut NullablePrecedenceCtx,
3561    continuations: &mut Vec<OperatorRuleContinuation>,
3562    visited: &mut BTreeSet<(usize, i32, bool)>,
3563) -> OperatorSymbolReachability {
3564    let key = (
3565        state_number,
3566        request.precedence,
3567        request.predicate_dependent,
3568    );
3569    if !visited.insert(key) {
3570        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3571    }
3572    let Some(state) = atn.state(state_number) else {
3573        visited.remove(&key);
3574        return OperatorSymbolReachability::default();
3575    };
3576    let mut reachability = OperatorSymbolReachability::default();
3577    for transition in &state.transitions() {
3578        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3579            reachability = reachability.union(state_operator_token_prefix_reachability(
3580                atn,
3581                transition.target(),
3582                request,
3583                continuations,
3584                &mut BTreeSet::new(),
3585            ));
3586            continue;
3587        }
3588        let transition_reachability = match &transition.data() {
3589            Transition::Rule {
3590                target,
3591                rule_index,
3592                follow_state,
3593                precedence: rule_precedence,
3594            } => {
3595                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3596                    continue;
3597                };
3598                continuations.push(OperatorRuleContinuation {
3599                    stop_state: child_stop,
3600                    follow_state: *follow_state,
3601                    return_precedence: request.precedence,
3602                });
3603                let mut result = state_can_reach_symbol_with_precedence(
3604                    atn,
3605                    *target,
3606                    OperatorReachabilityRequest {
3607                        precedence: *rule_precedence,
3608                        ..request
3609                    },
3610                    nullable_ctx,
3611                    continuations,
3612                    visited,
3613                );
3614                continuations.pop();
3615                if state_is_nullable_with_precedence(
3616                    atn,
3617                    *target,
3618                    child_stop,
3619                    *rule_precedence,
3620                    true,
3621                    nullable_ctx,
3622                ) {
3623                    let child_predicate_dependent = request.predicate_dependent
3624                        || !state_is_nullable_with_precedence(
3625                            atn,
3626                            *target,
3627                            child_stop,
3628                            *rule_precedence,
3629                            false,
3630                            nullable_ctx,
3631                        );
3632                    result = result.union(state_can_reach_symbol_with_precedence(
3633                        atn,
3634                        *follow_state,
3635                        OperatorReachabilityRequest {
3636                            predicate_dependent: child_predicate_dependent,
3637                            ..request
3638                        },
3639                        nullable_ctx,
3640                        continuations,
3641                        visited,
3642                    ));
3643                }
3644                result
3645            }
3646            Transition::Epsilon { target }
3647            | Transition::Action { target, .. }
3648            | Transition::Precedence { target, .. } => {
3649                if matches!(
3650                    &transition.data(),
3651                    Transition::Precedence {
3652                        precedence: transition_precedence,
3653                        ..
3654                    } if *transition_precedence < request.precedence
3655                ) {
3656                    continue;
3657                }
3658                state_can_reach_symbol_with_precedence(
3659                    atn,
3660                    *target,
3661                    request,
3662                    nullable_ctx,
3663                    continuations,
3664                    visited,
3665                )
3666            }
3667            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3668                atn,
3669                *target,
3670                OperatorReachabilityRequest {
3671                    predicate_dependent: true,
3672                    ..request
3673                },
3674                nullable_ctx,
3675                continuations,
3676                visited,
3677            ),
3678            Transition::Atom { .. }
3679            | Transition::Range { .. }
3680            | Transition::Set { .. }
3681            | Transition::NotSet { .. }
3682            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3683        };
3684        reachability = reachability.union(transition_reachability);
3685    }
3686    visited.remove(&key);
3687    reachability
3688}
3689
3690fn left_recursive_operator_lookahead(
3691    atn: &Atn,
3692    state_number: usize,
3693    precedence: i32,
3694) -> LeftRecursiveOperatorLookahead {
3695    let Some(state) = atn.state(state_number) else {
3696        return LeftRecursiveOperatorLookahead::default();
3697    };
3698    let Some(operator_rule_index) = state.rule_index() else {
3699        return LeftRecursiveOperatorLookahead::default();
3700    };
3701    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3702    let mut nullable_ctx = NullablePrecedenceCtx {
3703        cache: FxHashMap::default(),
3704        in_progress: BTreeSet::new(),
3705        hit_cycle: false,
3706    };
3707    for transition in &state.transitions() {
3708        let target = transition.target();
3709        if atn
3710            .state(target)
3711            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3712        {
3713            continue;
3714        }
3715        for symbol in 1..=atn.max_token_type() {
3716            let reachability = state_can_reach_symbol_with_precedence(
3717                atn,
3718                target,
3719                OperatorReachabilityRequest {
3720                    symbol,
3721                    precedence,
3722                    predicate_dependent: false,
3723                    operator_rule_index,
3724                },
3725                &mut nullable_ctx,
3726                &mut Vec::new(),
3727                &mut BTreeSet::new(),
3728            );
3729            if reachability.single_token {
3730                lookahead.single_token.insert(symbol);
3731            }
3732            if reachability.multi_token {
3733                lookahead.multi_token_prefix.insert(symbol);
3734            }
3735            if reachability.predicate_dependent {
3736                lookahead.predicate_dependent.insert(symbol);
3737            }
3738        }
3739    }
3740    lookahead
3741}
3742
3743#[derive(Debug, Default)]
3744struct StateBeforeStopLookahead {
3745    symbols: TokenBitSet,
3746    reaches_context_boundary: bool,
3747}
3748
3749fn state_before_stop_lookahead(
3750    atn: &Atn,
3751    state_number: usize,
3752    stop_state_number: usize,
3753) -> Rc<StateBeforeStopLookahead> {
3754    with_shared_atn_caches(atn, |cache| {
3755        let key = (state_number, stop_state_number);
3756        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3757            return Rc::clone(cached);
3758        }
3759        let mut lookahead = StateBeforeStopLookahead::default();
3760        state_before_stop_lookahead_inner(
3761            atn,
3762            state_number,
3763            stop_state_number,
3764            &mut BTreeSet::new(),
3765            &mut cache.first_set,
3766            &mut lookahead,
3767        );
3768        let lookahead = Rc::new(lookahead);
3769        cache
3770            .state_before_stop_lookahead
3771            .insert(key, Rc::clone(&lookahead));
3772        lookahead
3773    })
3774}
3775
3776fn state_before_stop_lookahead_inner(
3777    atn: &Atn,
3778    state_number: usize,
3779    stop_state_number: usize,
3780    visited: &mut BTreeSet<usize>,
3781    first_set_cache: &mut FirstSetCache,
3782    lookahead: &mut StateBeforeStopLookahead,
3783) {
3784    if state_number == stop_state_number {
3785        lookahead.reaches_context_boundary = true;
3786        return;
3787    }
3788    if !visited.insert(state_number) {
3789        return;
3790    }
3791    let Some(state) = atn.state(state_number) else {
3792        return;
3793    };
3794    if state.kind() == AtnStateKind::RuleStop {
3795        lookahead.reaches_context_boundary = true;
3796        return;
3797    }
3798    for transition in &state.transitions() {
3799        match &transition.data() {
3800            Transition::Epsilon { target }
3801            | Transition::Action { target, .. }
3802            | Transition::Predicate { target, .. }
3803            | Transition::Precedence { target, .. } => {
3804                state_before_stop_lookahead_inner(
3805                    atn,
3806                    *target,
3807                    stop_state_number,
3808                    visited,
3809                    first_set_cache,
3810                    lookahead,
3811                );
3812            }
3813            Transition::Rule {
3814                target,
3815                rule_index,
3816                follow_state,
3817                ..
3818            } => {
3819                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3820                    continue;
3821                };
3822                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3823                lookahead.symbols.extend_from(&child.symbols);
3824                if child.nullable {
3825                    state_before_stop_lookahead_inner(
3826                        atn,
3827                        *follow_state,
3828                        stop_state_number,
3829                        visited,
3830                        first_set_cache,
3831                        lookahead,
3832                    );
3833                }
3834            }
3835            Transition::Atom { .. }
3836            | Transition::Range { .. }
3837            | Transition::Set { .. }
3838            | Transition::NotSet { .. }
3839            | Transition::Wildcard { .. } => {
3840                lookahead.symbols.extend_iter(transition_expected_symbols(
3841                    transition,
3842                    atn.max_token_type(),
3843                ));
3844            }
3845        }
3846    }
3847}
3848
3849fn caller_context_can_match_symbol_before_state(
3850    atn: &Atn,
3851    return_states: impl DoubleEndedIterator<Item = usize>,
3852    stop_state_number: usize,
3853    symbol: i32,
3854) -> bool {
3855    for return_state in return_states.rev() {
3856        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3857        if lookahead.symbols.contains(symbol) {
3858            return true;
3859        }
3860        if !lookahead.reaches_context_boundary {
3861            return false;
3862        }
3863    }
3864    false
3865}
3866
3867/// Carries recovery expectations and their restart state through epsilon-only
3868/// paths. ANTLR can report and repair at the decision state even when the
3869/// failed consuming transition is nested under block or loop epsilon edges.
3870fn next_recovery_context(
3871    atn: &Atn,
3872    state: AtnState<'_>,
3873    inherited: &BTreeSet<i32>,
3874    inherited_state: Option<usize>,
3875) -> (BTreeSet<i32>, Option<usize>) {
3876    let state_symbols = state_expected_symbols(atn, state.state_number());
3877    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3878        let mut symbols = state_symbols;
3879        symbols.extend(inherited.iter().copied());
3880        return (symbols, Some(state.state_number()));
3881    }
3882    (inherited.clone(), inherited_state)
3883}
3884
3885fn recovery_expected_symbols(
3886    atn: &Atn,
3887    state_number: usize,
3888    inherited: &BTreeSet<i32>,
3889) -> BTreeSet<i32> {
3890    let mut symbols = state_expected_symbols(atn, state_number);
3891    symbols.extend(inherited.iter().copied());
3892    symbols
3893}
3894
3895/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3896/// parser's cached state-expected-symbols sets and the inherited `Rc`
3897/// without copying when the state cannot widen recovery.
3898fn fast_next_recovery_context<S, H>(
3899    parser: &mut BaseParser<S, H>,
3900    atn: &Atn,
3901    state: AtnState<'_>,
3902    inherited: &Rc<BTreeSet<i32>>,
3903    inherited_state: Option<usize>,
3904) -> (Rc<BTreeSet<i32>>, Option<usize>)
3905where
3906    S: TokenSource,
3907    H: SemanticHooks,
3908{
3909    if state.transitions().len() <= 1 {
3910        return (Rc::clone(inherited), inherited_state);
3911    }
3912    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3913    if state_symbols.is_empty() {
3914        return (Rc::clone(inherited), inherited_state);
3915    }
3916    if inherited.is_empty() {
3917        return (state_symbols, Some(state.state_number()));
3918    }
3919    if Rc::ptr_eq(&state_symbols, inherited) {
3920        return (state_symbols, Some(state.state_number()));
3921    }
3922    let mut combined = (*state_symbols).clone();
3923    combined.extend(inherited.iter().copied());
3924    (
3925        parser.intern_recovery_symbols(combined),
3926        Some(state.state_number()),
3927    )
3928}
3929
3930/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3931/// cached state-expected-symbols and avoids cloning when no widening is
3932/// needed.
3933fn fast_recovery_expected_symbols<S, H>(
3934    parser: &mut BaseParser<S, H>,
3935    atn: &Atn,
3936    state_number: usize,
3937    inherited: &Rc<BTreeSet<i32>>,
3938) -> Rc<BTreeSet<i32>>
3939where
3940    S: TokenSource,
3941    H: SemanticHooks,
3942{
3943    let cached = parser.cached_state_expected_symbols(atn, state_number);
3944    if inherited.is_empty() {
3945        return cached;
3946    }
3947    if cached.is_empty() {
3948        return Rc::clone(inherited);
3949    }
3950    if Rc::ptr_eq(&cached, inherited) {
3951        return cached;
3952    }
3953    let mut combined = (*cached).clone();
3954    combined.extend(inherited.iter().copied());
3955    parser.intern_recovery_symbols(combined)
3956}
3957
3958struct ParserTableSemCtx<'a> {
3959    member_values: &'a mut BTreeMap<usize, i64>,
3960    return_values: &'a mut BTreeMap<String, i64>,
3961}
3962
3963impl semir::PredContext for ParserTableSemCtx<'_> {
3964    type TokenText<'a>
3965        = &'a str
3966    where
3967        Self: 'a;
3968
3969    fn la(&mut self, _offset: isize) -> i64 {
3970        i64::from(TOKEN_EOF)
3971    }
3972
3973    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3974        None
3975    }
3976
3977    fn token_index_adjacent(&mut self) -> bool {
3978        false
3979    }
3980
3981    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3982        None
3983    }
3984
3985    fn member(&self, member: usize) -> Option<i64> {
3986        Some(self.member_values.get(&member).copied().unwrap_or_default())
3987    }
3988
3989    fn local_arg(&self) -> Option<i64> {
3990        None
3991    }
3992
3993    fn column(&self) -> Option<i64> {
3994        None
3995    }
3996
3997    fn token_start_column(&self) -> Option<i64> {
3998        None
3999    }
4000
4001    fn token_text_so_far(&self) -> Option<String> {
4002        None
4003    }
4004
4005    fn hook(&mut self, _hook: HookId) -> bool {
4006        false
4007    }
4008}
4009
4010impl semir::ActContext for ParserTableSemCtx<'_> {
4011    fn set_member(&mut self, member: usize, value: i64) {
4012        self.member_values.insert(member, value);
4013    }
4014
4015    fn set_return(&mut self, name: &str, value: i64) {
4016        self.return_values.insert(name.to_owned(), value);
4017    }
4018
4019    fn action_hook(&mut self, _hook: HookId) {}
4020}
4021
4022/// Applies generated integer-member side effects to one speculative path.
4023fn apply_member_actions(
4024    source_state: usize,
4025    actions: &[ParserMemberAction],
4026    semantics: Option<&ParserSemantics>,
4027    values: &mut BTreeMap<usize, i64>,
4028) {
4029    for action in actions
4030        .iter()
4031        .filter(|action| action.source_state == source_state)
4032    {
4033        *values.entry(action.member).or_default() += action.delta;
4034    }
4035    let Some(semantics) = semantics else {
4036        return;
4037    };
4038    let mut return_values = BTreeMap::new();
4039    let mut ctx = ParserTableSemCtx {
4040        member_values: values,
4041        return_values: &mut return_values,
4042    };
4043    for action in semantics
4044        .actions
4045        .iter()
4046        .filter(|action| action.source_state == source_state && action.speculative)
4047    {
4048        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
4049    }
4050}
4051
4052/// Returns the speculative member state after replaying one ATN action state.
4053fn member_values_after_action(
4054    source_state: usize,
4055    actions: &[ParserMemberAction],
4056    semantics: Option<&ParserSemantics>,
4057    values: &BTreeMap<usize, i64>,
4058) -> BTreeMap<usize, i64> {
4059    let mut values = values.clone();
4060    apply_member_actions(source_state, actions, semantics, &mut values);
4061    values
4062}
4063
4064/// Returns the speculative rule-return state after replaying one ATN action.
4065fn return_values_after_action(
4066    source_state: usize,
4067    rule_index: usize,
4068    actions: &[ParserReturnAction],
4069    semantics: Option<&ParserSemantics>,
4070    values: &BTreeMap<String, i64>,
4071) -> BTreeMap<String, i64> {
4072    let mut values = values.clone();
4073    for action in actions
4074        .iter()
4075        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
4076    {
4077        values.insert(action.name.to_owned(), action.value);
4078    }
4079    if let Some(semantics) = semantics {
4080        let mut member_values = BTreeMap::new();
4081        let mut ctx = ParserTableSemCtx {
4082            member_values: &mut member_values,
4083            return_values: &mut values,
4084        };
4085        for action in semantics.actions.iter().filter(|action| {
4086            action.source_state == source_state
4087                && action.rule_index == rule_index
4088                && !action.speculative
4089        }) {
4090            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
4091        }
4092    }
4093    values
4094}
4095
4096/// Resolves the integer argument visible to a child rule invocation.
4097fn rule_local_int_arg(
4098    rule_args: &[ParserRuleArg],
4099    source_state: usize,
4100    rule_index: usize,
4101    local_int_arg: Option<(usize, i64)>,
4102) -> Option<(usize, i64)> {
4103    rule_args
4104        .iter()
4105        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
4106        .map(|arg| {
4107            let value = if arg.inherit_local {
4108                local_int_arg.map_or(arg.value, |(_, value)| value)
4109            } else {
4110                arg.value
4111            };
4112            (rule_index, value)
4113        })
4114}
4115
4116/// Builds the terminal recognition outcome for a path that reached its stop
4117/// state.
4118fn stop_outcome(
4119    index: usize,
4120    consumed_eof: bool,
4121    rule_alt_number: usize,
4122    member_values: BTreeMap<usize, i64>,
4123    return_values: BTreeMap<String, i64>,
4124) -> Vec<RecognizeOutcome> {
4125    vec![RecognizeOutcome {
4126        index,
4127        consumed_eof,
4128        alt_number: rule_alt_number,
4129        member_values,
4130        return_values,
4131        diagnostics: DiagnosticSeqId::EMPTY,
4132        decisions: Vec::new(),
4133        actions: Vec::new(),
4134        nodes: NodeSeqId::EMPTY,
4135    }]
4136}
4137
4138fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
4139    with_shared_atn_caches(atn, |cache| {
4140        *cache.observable_action_transitions.get_or_insert_with(|| {
4141            atn.states().any(|state| {
4142                state.transitions().iter().any(|transition| {
4143                    matches!(
4144                        &transition.data(),
4145                        Transition::Action {
4146                            action_index: Some(_),
4147                            ..
4148                        }
4149                    )
4150                })
4151            })
4152        })
4153    })
4154}
4155
4156fn atn_has_predicate_transitions(atn: &Atn) -> bool {
4157    with_shared_atn_caches(atn, |cache| {
4158        *cache.predicate_transitions.get_or_insert_with(|| {
4159            atn.states().any(|state| {
4160                state
4161                    .transitions()
4162                    .iter()
4163                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
4164            })
4165        })
4166    })
4167}
4168
4169/// Reports whether predicates are the only observable semantics the fast
4170/// recognizer must preserve. Without path-local actions, arguments, or return
4171/// state, repeated evaluation at one coordinate and input index receives the
4172/// same runtime context.
4173fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
4174    options.init_action_rules.is_empty()
4175        && !options.track_alt_numbers
4176        && options
4177            .predicates
4178            .iter()
4179            .all(|(_, _, predicate)| predicate.failure_message().is_none())
4180        && options.semantics.is_none_or(|semantics| {
4181            semantics.actions.is_empty()
4182                && semantics
4183                    .predicates
4184                    .iter()
4185                    .all(|predicate| predicate.failure_message.is_none())
4186        })
4187        && options.rule_args.is_empty()
4188        && options.member_actions.is_empty()
4189        && options.return_actions.is_empty()
4190        && !atn_has_observable_action_transitions(atn)
4191}
4192
4193#[derive(Clone, Debug, Eq, PartialEq)]
4194struct RecognizeRequest<'a> {
4195    state_number: usize,
4196    stop_state: usize,
4197    index: usize,
4198    rule_start_index: usize,
4199    decision_start_index: Option<usize>,
4200    init_action_rules: &'a BTreeSet<usize>,
4201    predicates: &'a [(usize, usize, ParserPredicate)],
4202    semantics: Option<&'a ParserSemantics>,
4203    rule_args: &'a [ParserRuleArg],
4204    member_actions: &'a [ParserMemberAction],
4205    return_actions: &'a [ParserReturnAction],
4206    local_int_arg: Option<(usize, i64)>,
4207    member_values: BTreeMap<usize, i64>,
4208    return_values: BTreeMap<String, i64>,
4209    rule_alt_number: usize,
4210    track_alt_numbers: bool,
4211    consumed_eof: bool,
4212    committed_decision: bool,
4213    /// Current left-recursive precedence threshold, matching ANTLR's
4214    /// `precpred(_ctx, k)` check for generated precedence rules.
4215    precedence: i32,
4216    depth: usize,
4217    recovery_symbols: BTreeSet<i32>,
4218    recovery_state: Option<usize>,
4219}
4220
4221#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
4222struct RecognizeKey {
4223    state_number: usize,
4224    stop_state: usize,
4225    index: usize,
4226    rule_start_index: usize,
4227    decision_start_index: Option<usize>,
4228    local_int_arg: Option<(usize, i64)>,
4229    member_values: BTreeMap<usize, i64>,
4230    return_values: BTreeMap<String, i64>,
4231    rule_alt_number: usize,
4232    track_alt_numbers: bool,
4233    consumed_eof: bool,
4234    committed_decision: bool,
4235    precedence: i32,
4236    recovery_symbols: BTreeSet<i32>,
4237    recovery_state: Option<usize>,
4238}
4239
4240#[derive(Clone, Debug, Eq, PartialEq)]
4241struct EpsilonActionStep {
4242    source_state: usize,
4243    target: usize,
4244    action_rule_index: Option<usize>,
4245    left_recursive_boundary: Option<usize>,
4246    decision: Option<usize>,
4247    decision_start_index: Option<usize>,
4248    alt_number: usize,
4249    recovery_symbols: BTreeSet<i32>,
4250    recovery_state: Option<usize>,
4251}
4252
4253struct RecognizeScratch<'a> {
4254    visiting: &'a mut BTreeSet<RecognizeKey>,
4255    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4256    expected: &'a mut ExpectedTokens,
4257}
4258
4259#[derive(Clone, Debug, Eq, PartialEq)]
4260struct FastRecognizeRequest {
4261    state_number: usize,
4262    stop_state: usize,
4263    index: usize,
4264    rule_start_index: usize,
4265    decision_start_index: Option<usize>,
4266    precedence: i32,
4267    depth: usize,
4268    recovery_symbols: Rc<BTreeSet<i32>>,
4269    recovery_state: Option<usize>,
4270}
4271
4272#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4273struct FastRecognizeTopRequest {
4274    start_state: usize,
4275    stop_state: usize,
4276    start_index: usize,
4277    precedence: i32,
4278    caller_follow_state: Option<usize>,
4279}
4280
4281#[derive(Clone, Copy, Debug)]
4282struct FastPredicateContext<'a> {
4283    predicates: &'a [(usize, usize, ParserPredicate)],
4284    semantics: Option<&'a ParserSemantics>,
4285    member_values: &'a BTreeMap<usize, i64>,
4286}
4287
4288#[derive(Clone, Copy, Debug, Default)]
4289struct AltNumberTracking {
4290    public: bool,
4291    context: bool,
4292}
4293
4294impl AltNumberTracking {
4295    const fn any(self) -> bool {
4296        self.public || self.context
4297    }
4298}
4299
4300struct FastRecognizeScratch<'a, 'b> {
4301    predicate_context: Option<FastPredicateContext<'a>>,
4302    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4303    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4304    expected: &'b mut ExpectedTokens,
4305    native_depth: usize,
4306}
4307
4308#[derive(Clone, Copy, Debug)]
4309struct FastRepetitionShape {
4310    enter_target: usize,
4311    exit_target: usize,
4312    body_stop_state: usize,
4313    enter_transition_index: usize,
4314    exit_transition_index: usize,
4315}
4316
4317#[derive(Clone, Copy, Debug)]
4318struct FastRepetitionPath {
4319    index: usize,
4320    deferred_nodes: FastDeferredNodeId,
4321    diagnostics: DiagnosticSeqId,
4322    consumed_eof: bool,
4323}
4324
4325enum FastRepetitionWork {
4326    Enter(FastRepetitionPath),
4327    Exit(FastRepetitionPath),
4328}
4329
4330/// Dense entered/exited coordinate sets for one repetition walk.
4331///
4332/// The start coordinate stays inline so short loops avoid a heap allocation;
4333/// later token indexes use one byte each instead of two hash-table entries.
4334struct FastRepetitionCoordinates {
4335    base_index: usize,
4336    base_state: u8,
4337    later_states: Vec<u8>,
4338}
4339
4340impl FastRepetitionCoordinates {
4341    const ENTERED: u8 = 0;
4342    const EXITED: u8 = 2;
4343
4344    const fn new(base_index: usize) -> Self {
4345        Self {
4346            base_index,
4347            base_state: 0,
4348            later_states: Vec::new(),
4349        }
4350    }
4351
4352    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4353        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4354    }
4355
4356    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4357        self.insert(path.index, path.consumed_eof, Self::EXITED)
4358    }
4359
4360    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4361        let Some(offset) = index.checked_sub(self.base_index) else {
4362            return false;
4363        };
4364        let state = if offset == 0 {
4365            &mut self.base_state
4366        } else {
4367            if self.later_states.len() < offset {
4368                self.later_states.resize(offset, 0);
4369            }
4370            &mut self.later_states[offset - 1]
4371        };
4372        let bit = 1 << (base_bit + u8::from(consumed_eof));
4373        let is_new = *state & bit == 0;
4374        *state |= bit;
4375        is_new
4376    }
4377}
4378
4379fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4380    if state.precedence_rule_decision()
4381        || !matches!(
4382            state.kind(),
4383            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4384        )
4385        || state.transitions().len() != 2
4386    {
4387        return None;
4388    }
4389    let mut enter = None;
4390    let mut exit = None;
4391    for (index, transition) in state.transitions().iter().enumerate() {
4392        if transition.kind() != ParserTransitionKind::Epsilon {
4393            return None;
4394        }
4395        let target = transition.target();
4396        if atn
4397            .state(target)
4398            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4399        {
4400            if exit.replace((index, target)).is_some() {
4401                return None;
4402            }
4403        } else if enter.replace((index, target)).is_some() {
4404            return None;
4405        }
4406    }
4407    let (enter_transition_index, enter_target) = enter?;
4408    let (exit_transition_index, exit_target) = exit?;
4409    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4410        atn.state(exit_target)?.loop_back_state()?
4411    } else {
4412        state.state_number()
4413    };
4414    Some(FastRepetitionShape {
4415        enter_target,
4416        exit_target,
4417        body_stop_state,
4418        enter_transition_index,
4419        exit_transition_index,
4420    })
4421}
4422
4423fn push_fast_repetition_work(
4424    work: &mut Vec<FastRepetitionWork>,
4425    shape: FastRepetitionShape,
4426    path: FastRepetitionPath,
4427    lookahead: Option<&DecisionLookahead>,
4428    symbol: i32,
4429) {
4430    // Match the normal recognizer's FIRST-set pruning before queueing work.
4431    // Ambiguous body paths still share the coordinate bitmap below.
4432    let transition_is_viable = |transition_index: usize| {
4433        let Some(entry) = lookahead else {
4434            return true;
4435        };
4436        let Some(transition) = entry.transitions.get(transition_index) else {
4437            return true;
4438        };
4439        transition.nullable || transition.symbols.contains(symbol)
4440    };
4441    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4442    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4443    if shape.enter_transition_index < shape.exit_transition_index {
4444        if exit_is_viable {
4445            work.push(FastRepetitionWork::Exit(path));
4446        }
4447        if enter_is_viable {
4448            work.push(FastRepetitionWork::Enter(path));
4449        }
4450    } else {
4451        if enter_is_viable {
4452            work.push(FastRepetitionWork::Enter(path));
4453        }
4454        if exit_is_viable {
4455            work.push(FastRepetitionWork::Exit(path));
4456        }
4457    }
4458}
4459
4460/// Memo key for the fast recognizer. `recovery_symbols` must come from
4461/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4462/// key, so equal sets share one allocation and the key can store that
4463/// allocation's address instead of cloning an `Rc` and walking the full
4464/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4465/// into distinct cache coordinates.
4466#[derive(Clone, Debug)]
4467struct FastRecognizeKey {
4468    state_number: usize,
4469    stop_state: usize,
4470    index: usize,
4471    rule_start_index: usize,
4472    decision_start_index: Option<usize>,
4473    precedence: i32,
4474    recovery_symbols_id: usize,
4475    recovery_state: Option<usize>,
4476}
4477
4478impl PartialEq for FastRecognizeKey {
4479    fn eq(&self, other: &Self) -> bool {
4480        if self.state_number != other.state_number
4481            || self.stop_state != other.stop_state
4482            || self.index != other.index
4483            || self.rule_start_index != other.rule_start_index
4484            || self.decision_start_index != other.decision_start_index
4485            || self.precedence != other.precedence
4486            || self.recovery_state != other.recovery_state
4487            || self.recovery_symbols_id != other.recovery_symbols_id
4488        {
4489            return false;
4490        }
4491        true
4492    }
4493}
4494
4495impl Eq for FastRecognizeKey {}
4496
4497impl Hash for FastRecognizeKey {
4498    fn hash<H: Hasher>(&self, hasher: &mut H) {
4499        self.state_number.hash(hasher);
4500        self.stop_state.hash(hasher);
4501        self.index.hash(hasher);
4502        self.rule_start_index.hash(hasher);
4503        self.decision_start_index.hash(hasher);
4504        self.precedence.hash(hasher);
4505        self.recovery_state.hash(hasher);
4506        self.recovery_symbols_id.hash(hasher);
4507    }
4508}
4509
4510struct FastRecoveryRequest<'a, 'b> {
4511    atn: &'a Atn,
4512    transition: ParserTransition<'a>,
4513    expected_symbols: Rc<BTreeSet<i32>>,
4514    target: usize,
4515    request: FastRecognizeRequest,
4516    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4517    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4518    expected: &'b mut ExpectedTokens,
4519}
4520
4521struct FastCurrentTokenDeletionRequest<'a, 'b> {
4522    atn: &'a Atn,
4523    expected_symbols: Rc<BTreeSet<i32>>,
4524    request: FastRecognizeRequest,
4525    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4526    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4527    expected: &'b mut ExpectedTokens,
4528}
4529
4530#[derive(Clone, Copy)]
4531struct FastChildRuleFailureRecoveryRequest<'a> {
4532    atn: &'a Atn,
4533    rule_index: usize,
4534    start_index: usize,
4535    follow_state: usize,
4536    stop_state: usize,
4537    expected: &'a ExpectedTokens,
4538}
4539
4540struct RecoveryRequest<'a, 'b> {
4541    atn: &'a Atn,
4542    transition: ParserTransition<'a>,
4543    expected_symbols: BTreeSet<i32>,
4544    target: usize,
4545    request: RecognizeRequest<'a>,
4546    visiting: &'b mut BTreeSet<RecognizeKey>,
4547    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4548    expected: &'b mut ExpectedTokens,
4549}
4550
4551struct CurrentTokenDeletionRequest<'a, 'b> {
4552    atn: &'a Atn,
4553    expected_symbols: BTreeSet<i32>,
4554    request: RecognizeRequest<'a>,
4555    visiting: &'b mut BTreeSet<RecognizeKey>,
4556    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4557    expected: &'b mut ExpectedTokens,
4558}
4559
4560/// Carries the state needed after the normal token-recovery strategies fail
4561/// for a consuming transition.
4562struct ConsumingFailureFallback<'a> {
4563    atn: &'a Atn,
4564    target: usize,
4565    request: RecognizeRequest<'a>,
4566    symbol: i32,
4567    expected_symbols: BTreeSet<i32>,
4568    decision_start_index: Option<usize>,
4569    decision: Option<usize>,
4570}
4571
4572/// Captures the parent-rule context needed when a called rule fails before it
4573/// can produce a normal outcome.
4574struct ChildRuleFailureRecovery<'a> {
4575    atn: &'a Atn,
4576    rule_index: usize,
4577    start_index: usize,
4578    follow_state: usize,
4579    stop_state: usize,
4580    member_values: BTreeMap<usize, i64>,
4581    expected: &'a ExpectedTokens,
4582}
4583
4584/// Bundles the context needed to evaluate one semantic predicate transition.
4585#[derive(Clone, Copy, Debug)]
4586struct PredicateEval<'a> {
4587    index: usize,
4588    rule_index: usize,
4589    pred_index: usize,
4590    predicates: &'a [(usize, usize, ParserPredicate)],
4591    semantics: Option<&'a ParserSemantics>,
4592    context: Option<&'a ParserRuleContext>,
4593    local_int_arg: Option<(usize, i64)>,
4594    member_values: &'a BTreeMap<usize, i64>,
4595}
4596
4597#[derive(Clone, Copy, Debug)]
4598struct ParserSemanticHookRequest<'a> {
4599    index: usize,
4600    rule_index: usize,
4601    pred_index: usize,
4602    context: Option<&'a ParserRuleContext>,
4603    local_int_arg: Option<(usize, i64)>,
4604    member_values: &'a BTreeMap<usize, i64>,
4605}
4606
4607/// Predicate-evaluation context over the recognizer's speculative state.
4608///
4609/// This sits in the prediction hot loop, so everything is borrowed: member
4610/// state read-only from the current speculative path and the rule name
4611/// straight from recognizer metadata. Predicates are pure by construction
4612/// ([`semir::PExpr`] has no mutating node); statement execution uses
4613/// [`ParserTableSemCtx`] (speculative member/return replay) and
4614/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4615struct ParserSemIrCtx<'a, S, H>
4616where
4617    S: TokenSource,
4618    H: SemanticHooks,
4619{
4620    input: &'a mut CommonTokenStream<S>,
4621    tree_storage: &'a ParseTreeStorage,
4622    semantic_hooks: &'a mut H,
4623    rule_index: usize,
4624    coordinate_index: usize,
4625    rule_name: Option<&'a str>,
4626    context: Option<&'a ParserRuleContext>,
4627    local_int_arg: Option<(usize, i64)>,
4628    member_values: &'a BTreeMap<usize, i64>,
4629    invoked_predicates: &'a mut Vec<(usize, usize)>,
4630    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4631    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4632    /// table path instead of coercing the miss to `false`.
4633    unknown_predicate_policy: UnknownSemanticPolicy,
4634    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4635}
4636
4637impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4638where
4639    S: TokenSource,
4640    H: SemanticHooks,
4641{
4642    type TokenText<'a>
4643        = TokenView<'a>
4644    where
4645        Self: 'a;
4646
4647    fn la(&mut self, offset: isize) -> i64 {
4648        i64::from(self.input.la(offset))
4649    }
4650
4651    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4652        self.input.lt(offset)
4653    }
4654
4655    fn token_index_adjacent(&mut self) -> bool {
4656        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4657            return false;
4658        };
4659        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4660            return false;
4661        };
4662        first + 1 == second
4663    }
4664
4665    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4666        self.context.and_then(|context| {
4667            context
4668                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4669                .next()
4670                .map(crate::tree::RuleNodeView::text)
4671        })
4672    }
4673
4674    fn member(&self, member: usize) -> Option<i64> {
4675        Some(self.member_values.get(&member).copied().unwrap_or_default())
4676    }
4677
4678    fn local_arg(&self) -> Option<i64> {
4679        self.local_int_arg.map(|(_, value)| value)
4680    }
4681
4682    fn column(&self) -> Option<i64> {
4683        None
4684    }
4685
4686    fn token_start_column(&self) -> Option<i64> {
4687        None
4688    }
4689
4690    fn token_text_so_far(&self) -> Option<String> {
4691        None
4692    }
4693
4694    fn hook(&mut self, _hook: HookId) -> bool {
4695        let mut ctx = ParserSemCtx {
4696            input: &mut *self.input,
4697            tree_storage: self.tree_storage,
4698            rule_index: self.rule_index,
4699            coordinate_index: self.coordinate_index,
4700            rule_name: self.rule_name.map(str::to_owned),
4701            context: self.context,
4702            tree: None,
4703            local_int_arg: self.local_int_arg,
4704            member_values: self.member_values,
4705            action: None,
4706        };
4707        match self
4708            .semantic_hooks
4709            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4710        {
4711            Some(result) => result,
4712            // No hook answered this coordinate: fall through to the configured
4713            // policy instead of silently rejecting the alternative, matching the
4714            // legacy table path's dispatch chain (hook → policy).
4715            None => apply_unknown_predicate_policy(
4716                self.unknown_predicate_policy,
4717                self.rule_index,
4718                self.coordinate_index,
4719                self.unknown_predicate_hits,
4720            ),
4721        }
4722    }
4723
4724    fn trace_bool(&mut self, value: bool) -> bool {
4725        let key = (self.rule_index, self.coordinate_index);
4726        if !self.invoked_predicates.contains(&key) {
4727            self.invoked_predicates.push(key);
4728            use std::io::Write as _;
4729            let mut stdout = std::io::stdout().lock();
4730            let _ = writeln!(stdout, "eval={value}");
4731        }
4732        value
4733    }
4734}
4735
4736/// Captures predicate-failure recovery metadata for fail-option predicates.
4737struct PredicateFailureRecovery<'a> {
4738    rule_index: usize,
4739    index: usize,
4740    message: &'a str,
4741    member_values: BTreeMap<usize, i64>,
4742    return_values: BTreeMap<String, i64>,
4743    rule_alt_number: usize,
4744}
4745
4746#[derive(Debug)]
4747enum DirectAdaptiveParseControl {
4748    Fallback(DirectAdaptiveFallback),
4749}
4750
4751#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4752enum DirectAdaptiveFallback {
4753    Action,
4754    InvalidAlt,
4755    LeftRecursiveBoundary,
4756    MissingAtn,
4757    NoTransition,
4758    Predicate,
4759    Prediction,
4760    Precedence,
4761    RuleStop,
4762    SemanticContext,
4763    StepLimit,
4764    TokenMismatch,
4765    UnknownDecision,
4766}
4767
4768type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4769
4770struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4771where
4772    S: TokenSource,
4773    H: SemanticHooks,
4774{
4775    parser: &'sim mut BaseParser<S, H>,
4776    atn: &'atn Atn,
4777    simulator: &'sim mut ParserAtnSimulator<'atn>,
4778    decision_by_state: Vec<Option<usize>>,
4779    steps: usize,
4780}
4781
4782/// Outcome of a generated token / set / not-set match that may recover.
4783///
4784/// Generated parsers append `children` to the current rule context. `consumed_eof`
4785/// reports whether the match actually consumed a real EOF terminal — it is true
4786/// only on a successful match (or single-token deletion that lands on EOF), and
4787/// always false on single-token insertion, which synthesizes a missing token and
4788/// consumes nothing. Generated code feeds this into `finish_rule`'s
4789/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4790/// truly matched, matching ANTLR's `matchedEOF` semantics.
4791#[derive(Clone, Debug, Eq, PartialEq)]
4792pub struct GeneratedMatch {
4793    children: GeneratedMatchChildren,
4794    consumed_eof: bool,
4795}
4796
4797#[derive(Clone, Copy)]
4798enum GeneratedExpectedSymbols<'a> {
4799    Tree(&'a BTreeSet<i32>),
4800    TokenSet(ParserIntervalSet<'a>),
4801    TokenSetComplement {
4802        set: ParserIntervalSet<'a>,
4803        min_vocabulary: i32,
4804        max_vocabulary: i32,
4805    },
4806}
4807
4808impl GeneratedExpectedSymbols<'_> {
4809    fn is_empty(self) -> bool {
4810        match self {
4811            Self::Tree(symbols) => symbols.is_empty(),
4812            Self::TokenSet(set) => set.is_empty(),
4813            Self::TokenSetComplement {
4814                set,
4815                min_vocabulary,
4816                max_vocabulary,
4817            } => (min_vocabulary..=max_vocabulary).all(|symbol| set.contains(symbol)),
4818        }
4819    }
4820
4821    fn first(self) -> Option<i32> {
4822        match self {
4823            Self::Tree(symbols) => symbols.iter().next().copied(),
4824            Self::TokenSet(set) => set.ranges().next().map(|(start, _)| start),
4825            Self::TokenSetComplement {
4826                set,
4827                min_vocabulary,
4828                max_vocabulary,
4829            } => (min_vocabulary..=max_vocabulary).find(|symbol| !set.contains(*symbol)),
4830        }
4831    }
4832
4833    fn display(self, vocabulary: &Vocabulary) -> String {
4834        match self {
4835            Self::Tree(symbols) => expected_symbols_display(symbols, vocabulary),
4836            Self::TokenSet(set) => expected_symbols_display_iter(
4837                set.ranges().flat_map(|(start, stop)| start..=stop),
4838                vocabulary,
4839            ),
4840            Self::TokenSetComplement {
4841                set,
4842                min_vocabulary,
4843                max_vocabulary,
4844            } => expected_symbols_display_iter(
4845                (min_vocabulary..=max_vocabulary).filter(|symbol| !set.contains(*symbol)),
4846                vocabulary,
4847            ),
4848        }
4849    }
4850}
4851
4852#[derive(Clone, Debug, Eq, PartialEq)]
4853enum GeneratedMatchChildren {
4854    One(ParseTree),
4855    Many(Vec<ParseTree>),
4856}
4857
4858struct GeneratedMatchChildrenIntoIter {
4859    one: Option<ParseTree>,
4860    many: Option<std::vec::IntoIter<ParseTree>>,
4861}
4862
4863impl Iterator for GeneratedMatchChildrenIntoIter {
4864    type Item = ParseTree;
4865
4866    fn next(&mut self) -> Option<Self::Item> {
4867        self.one
4868            .take()
4869            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4870    }
4871}
4872
4873impl GeneratedMatch {
4874    /// Parse-tree children produced by the match (the matched terminal, an
4875    /// error node plus deleted-then-matched terminal, or a single missing-token
4876    /// error node).
4877    #[must_use]
4878    pub fn children(&self) -> &[ParseTree] {
4879        match &self.children {
4880            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4881            GeneratedMatchChildren::Many(children) => children,
4882        }
4883    }
4884
4885    /// Consumes the result, returning the children for appending to the rule
4886    /// context.
4887    #[must_use]
4888    pub fn into_children(self) -> Vec<ParseTree> {
4889        match self.children {
4890            GeneratedMatchChildren::One(child) => vec![child],
4891            GeneratedMatchChildren::Many(children) => children,
4892        }
4893    }
4894
4895    /// Consumes the match without allocating for the common single-child case.
4896    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4897        match self.children {
4898            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4899                one: Some(child),
4900                many: None,
4901            },
4902            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4903                one: None,
4904                many: Some(children.into_iter()),
4905            },
4906        }
4907    }
4908
4909    /// Whether a real EOF terminal was consumed by this match.
4910    #[must_use]
4911    pub const fn consumed_eof(&self) -> bool {
4912        self.consumed_eof
4913    }
4914}
4915
4916impl<S> BaseParser<S, NoSemanticHooks>
4917where
4918    S: TokenSource,
4919{
4920    /// Creates a parser base over a buffered token stream and recognizer
4921    /// metadata.
4922    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4923        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4924    }
4925}
4926
4927impl<S, H> BaseParser<S, H>
4928where
4929    S: TokenSource,
4930    H: SemanticHooks,
4931{
4932    /// Creates a parser base with caller-owned semantic hooks.
4933    pub fn with_semantic_hooks(
4934        input: CommonTokenStream<S>,
4935        data: RecognizerData,
4936        semantic_hooks: H,
4937    ) -> Self {
4938        Self {
4939            input,
4940            tree: ParseTreeStorage::new(),
4941            data,
4942            semantic_hooks,
4943            decision_override_generation: 0,
4944            build_parse_trees: true,
4945            syntax_errors: 0,
4946            report_diagnostic_errors: false,
4947            prediction_mode: PredictionMode::Ll,
4948            prediction_diagnostics: Vec::new(),
4949            reported_prediction_diagnostics: BTreeSet::new(),
4950            generated_parser_diagnostics: Vec::new(),
4951            generated_sync_expected: None,
4952            generated_recovery_error_index: None,
4953            generated_recovery_error_states: BTreeSet::new(),
4954            int_members: BTreeMap::new(),
4955            rule_context_stack: Vec::new(),
4956            rule_context_version: 0,
4957            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4958            pending_invoking_states: Vec::new(),
4959            precedence_stack: vec![0],
4960            invoked_predicates: Vec::new(),
4961            bail_on_error: false,
4962            parse_listeners: Vec::new(),
4963            parse_listener_abort: None,
4964            max_rule_depth: None,
4965            rule_depth_error: None,
4966            recursion_expansions: 0,
4967            recursion_expansion_marks: Vec::new(),
4968            unknown_predicate_policy: UnknownSemanticPolicy::default(),
4969            unknown_predicate_hits: Vec::new(),
4970            unhandled_action_hits: Vec::new(),
4971            rule_first_set_cache: Vec::new(),
4972            state_expected_cache: FxHashMap::default(),
4973            state_expected_token_cache: FxHashMap::default(),
4974            rule_stop_reach_cache: Vec::new(),
4975            recovery_symbols_intern: FxHashMap::default(),
4976            decision_lookahead_cache: FxHashMap::default(),
4977            ll1_decision_cache: FxHashMap::default(),
4978            fast_predicate_cache: FxHashMap::default(),
4979            empty_cycle_cache: Vec::new(),
4980            empty_cycle_cache_atn: None,
4981            clean_memo_mode: CleanMemoMode::Probe,
4982            clean_memo_probe_seen: FxHashSet::default(),
4983            clean_memo_probe_samples: 0,
4984            clean_memo_probe_repeats: 0,
4985            clean_memo_sparse_samples: 0,
4986            fast_recognize_scratch: FastRecognizeTopScratch::default(),
4987            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
4988            empty_recovery_symbols: Rc::new(BTreeSet::new()),
4989            fast_first_set_prefilter: true,
4990            fast_recovery_enabled: true,
4991            fast_token_nodes_enabled: true,
4992            fast_track_alt_numbers: false,
4993            recognition_arena: RecognitionArena::default(),
4994            last_recognition_arena_root: NodeSeqId::EMPTY,
4995            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
4996        }
4997    }
4998
4999    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
5000        &mut self.input
5001    }
5002
5003    /// Fully resets parser-owned state and rewinds the current token stream.
5004    ///
5005    /// Parser configuration, semantic hooks, learned DFA tables, and
5006    /// grammar-owned member values are retained.
5007    pub fn reset(&mut self) {
5008        self.input.seek(0);
5009        self.tree.reset();
5010        self.data.set_state(-1);
5011        self.syntax_errors = 0;
5012        self.prediction_diagnostics.clear();
5013        self.reported_prediction_diagnostics.clear();
5014        self.generated_parser_diagnostics.clear();
5015        self.generated_sync_expected = None;
5016        self.reset_generated_recovery_state();
5017        self.rule_context_stack.clear();
5018        self.advance_rule_context_version();
5019        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
5020        self.pending_invoking_states.clear();
5021        self.precedence_stack.clear();
5022        self.precedence_stack.push(0);
5023        self.invoked_predicates.clear();
5024        self.decision_override_generation = 0;
5025        self.unknown_predicate_hits.clear();
5026        self.unhandled_action_hits.clear();
5027        self.parse_listener_abort = None;
5028        self.rule_depth_error = None;
5029        self.recursion_expansions = 0;
5030        self.recursion_expansion_marks.clear();
5031        self.reset_per_parse_caches();
5032        self.fast_first_set_prefilter = true;
5033        self.fast_recovery_enabled = true;
5034        self.fast_token_nodes_enabled = self.build_parse_trees;
5035        self.fast_track_alt_numbers = false;
5036        self.reset_recognition_arena();
5037    }
5038
5039    /// Replaces the buffered token stream and fully resets this parser.
5040    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
5041        self.input = input;
5042        self.reset();
5043    }
5044
5045    /// Installs the policy for predicate coordinates that no translated table
5046    /// entry or user hook resolves.
5047    ///
5048    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
5049    /// but generated recursive-descent rules evaluate predicates directly
5050    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
5051    /// going through those options. Generated parser constructors call this so
5052    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
5053    /// the field at its `AssumeTrue` default and silently accepting an
5054    /// unimplemented hook predicate.
5055    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
5056        self.unknown_predicate_policy = policy;
5057    }
5058
5059    /// Reports any unknown predicate coordinate the generated-direct path
5060    /// recorded under [`UnknownSemanticPolicy::Error`], as an
5061    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
5062    /// after a rule completes so the fail-loud policy surfaces on the
5063    /// generated path the same way the interpreter entry surfaces it.
5064    #[must_use]
5065    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
5066        let error = self.unknown_semantic_error();
5067        self.unknown_predicate_hits.clear();
5068        self.unhandled_action_hits.clear();
5069        error
5070    }
5071
5072    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
5073    ///
5074    /// Generated parsers call this at the true top-level entry so a parser
5075    /// reused after a fail-loud (or recovered) parse starts clean, without
5076    /// clearing hits mid-parse where a generated parent still needs a child's
5077    /// recorded coordinate to survive to the top-level boundary.
5078    pub fn reset_unknown_semantic_hits(&mut self) {
5079        self.unknown_predicate_hits.clear();
5080        self.unhandled_action_hits.clear();
5081    }
5082
5083    /// Returns the token stream owned by this parser.
5084    #[must_use]
5085    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
5086        &self.input
5087    }
5088
5089    /// Returns the token stream for source replacement or in-place re-feeding.
5090    #[must_use]
5091    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
5092        &mut self.input
5093    }
5094
5095    /// Returns the canonical token store referenced by parse trees.
5096    #[must_use]
5097    pub const fn token_store(&self) -> &TokenStore {
5098        self.input.token_store()
5099    }
5100
5101    /// Returns the flat CST storage populated by completed rules.
5102    #[must_use]
5103    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
5104        &self.tree
5105    }
5106
5107    /// Resolves a compact parse-tree ID into a borrowing node view.
5108    #[must_use]
5109    pub fn node(&self, id: NodeId) -> Node<'_> {
5110        self.tree
5111            .node(self.input.token_store(), id)
5112            .expect("parser-produced node ID should remain valid")
5113    }
5114
5115    /// Consumes this parser and returns its token stream.
5116    #[must_use]
5117    pub fn into_token_stream(self) -> CommonTokenStream<S> {
5118        self.input
5119    }
5120
5121    /// Consumes this parser and returns its canonical token store.
5122    #[must_use]
5123    pub fn into_token_store(self) -> TokenStore {
5124        self.input.into_token_store()
5125    }
5126
5127    /// Consumes the parser and pairs its token store and flat CST with `root`.
5128    #[must_use]
5129    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
5130        ParsedFile::new(self.input.into_token_store(), self.tree, root)
5131    }
5132
5133    /// Returns the number of parser syntax errors recorded by committed parse
5134    /// paths so far.
5135    pub const fn number_of_syntax_errors(&self) -> usize {
5136        self.syntax_errors
5137    }
5138
5139    /// Computes reachability and retained-capacity counters for the most recent
5140    /// interpreted-rule recognition arena.
5141    ///
5142    /// The reachability scan is linear in the arena size and is deferred until
5143    /// this instrumentation method is called.
5144    #[must_use]
5145    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
5146        self.recognition_arena.stats(
5147            self.last_recognition_arena_root,
5148            self.last_recognition_arena_diagnostics,
5149        )
5150    }
5151
5152    /// Records a syntax error that generated parser code returns as fatal before
5153    /// it can recover into the current rule context.
5154    pub const fn record_generated_syntax_error(&mut self) {
5155        self.record_syntax_errors(1);
5156    }
5157
5158    const fn record_syntax_errors(&mut self, count: usize) {
5159        self.syntax_errors = self.syntax_errors.saturating_add(count);
5160    }
5161
5162    /// Emits diagnostics buffered by the token stream while generated parser
5163    /// code was fetching lexer tokens directly.
5164    pub fn report_token_source_errors(&mut self) {
5165        let errors = self.input.drain_source_errors();
5166        self.dispatch_token_source_errors(&errors);
5167    }
5168
5169    /// Captures generated-parser diagnostics and syntax-error count before a
5170    /// speculative generated rule path.
5171    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
5172        GeneratedDiagnosticsCheckpoint {
5173            diagnostics_len: self.generated_parser_diagnostics.len(),
5174            syntax_errors: self.syntax_errors,
5175            tree: self.tree.checkpoint(),
5176        }
5177    }
5178
5179    /// Restores generated-parser diagnostics after a speculative rule path failed.
5180    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
5181        self.generated_parser_diagnostics
5182            .truncate(marker.diagnostics_len);
5183        self.syntax_errors = marker.syntax_errors;
5184        self.generated_sync_expected = None;
5185        self.tree.rollback(marker.tree);
5186    }
5187
5188    /// Emits diagnostics recorded by committed generated parser recovery.
5189    pub fn report_generated_parser_diagnostics(&mut self) {
5190        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
5191        let token_errors = self.input.drain_source_errors();
5192        self.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
5193    }
5194
5195    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
5196        let offending = diagnostic
5197            .offending
5198            .and_then(|token| self.token_store().view(token));
5199        self.notify_error_listeners(
5200            offending,
5201            diagnostic.line,
5202            diagnostic.column,
5203            &diagnostic.message,
5204            None,
5205        );
5206    }
5207
5208    fn dispatch_parser_diagnostics<'a>(
5209        &self,
5210        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
5211    ) {
5212        for diagnostic in diagnostics {
5213            self.dispatch_parser_diagnostic(diagnostic);
5214        }
5215    }
5216
5217    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
5218        if self.input.token_source().report_error(source_error) {
5219            return;
5220        }
5221        // Lexer errors have no offending token: the failure is that no token
5222        // could be produced, matching ANTLR's null offendingSymbol.
5223        self.notify_error_listeners(
5224            None,
5225            source_error.line,
5226            source_error.column,
5227            &source_error.message,
5228            None,
5229        );
5230    }
5231
5232    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
5233        for error in errors {
5234            self.dispatch_token_source_error(error);
5235        }
5236    }
5237
5238    /// Dispatches generated parser and lexer diagnostics in the same
5239    /// source-position order as ANTLR's lazy token stream reports them.
5240    fn dispatch_generated_diagnostics(
5241        &self,
5242        parser_diagnostics: &[ParserDiagnostic],
5243        token_errors: &[TokenSourceError],
5244    ) {
5245        // Parser diagnostics keep their event order: Java's console and
5246        // DiagnosticErrorListener print reports as prediction produces them,
5247        // so reportAttemptingFullContext precedes reportContextSensitivity
5248        // even though the latter's position is earlier. Buffered token-source
5249        // errors interleave by source position and win ties.
5250        let mut token_iter = token_errors.iter().peekable();
5251        for diagnostic in parser_diagnostics {
5252            while let Some(error) = token_iter.peek() {
5253                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
5254                    self.dispatch_token_source_error(error);
5255                    token_iter.next();
5256                } else {
5257                    break;
5258                }
5259            }
5260            self.dispatch_parser_diagnostic(diagnostic);
5261        }
5262        for error in token_iter {
5263            self.dispatch_token_source_error(error);
5264        }
5265    }
5266
5267    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
5268    /// decision code.
5269    pub fn record_generated_ambiguity_diagnostic(
5270        &mut self,
5271        atn: &Atn,
5272        state_number: usize,
5273        start_index: usize,
5274        stop_index: usize,
5275        alts: &[usize],
5276    ) {
5277        if !self.report_diagnostic_errors || alts.len() < 2 {
5278            return;
5279        }
5280        let Some(decision) = atn
5281            .decision_to_state()
5282            .iter()
5283            .position(|candidate| candidate == state_number)
5284        else {
5285            return;
5286        };
5287        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5288            return;
5289        };
5290        let rule_name = self
5291            .rule_names()
5292            .get(rule_index)
5293            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5294        let input = display_input_text(&self.input.text(start_index, stop_index));
5295        let alts = alts
5296            .iter()
5297            .map(usize::to_string)
5298            .collect::<Vec<_>>()
5299            .join(", ");
5300        let key = (decision, start_index, format!("{alts}:{input}"));
5301        if !self.reported_prediction_diagnostics.insert(key) {
5302            return;
5303        }
5304        let start_diagnostic = diagnostic_for_token(
5305            self.token_at(start_index),
5306            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5307        );
5308        let stop_diagnostic = diagnostic_for_token(
5309            self.token_at(stop_index),
5310            format!(
5311                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5312            ),
5313        );
5314        self.generated_parser_diagnostics.push(start_diagnostic);
5315        self.generated_parser_diagnostics.push(stop_diagnostic);
5316    }
5317
5318    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5319    /// parser calls to the adaptive simulator.
5320    pub fn record_generated_prediction_diagnostic(
5321        &mut self,
5322        atn: &Atn,
5323        state_number: usize,
5324        prediction: &ParserAtnPrediction,
5325    ) {
5326        let Some(diagnostic) = &prediction.diagnostic else {
5327            return;
5328        };
5329        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5330            return;
5331        }
5332        let Some(decision) = atn
5333            .decision_to_state()
5334            .iter()
5335            .position(|candidate| candidate == state_number)
5336        else {
5337            return;
5338        };
5339        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5340            return;
5341        };
5342        let rule_name = self
5343            .rule_names()
5344            .get(rule_index)
5345            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5346        let attempt_input = display_input_text(
5347            &self
5348                .input
5349                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5350        );
5351        let result_input = display_input_text(
5352            &self
5353                .input
5354                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5355        );
5356        let alts = diagnostic
5357            .conflicting_alts
5358            .iter()
5359            .map(usize::to_string)
5360            .collect::<Vec<_>>()
5361            .join(", ");
5362        let key = (
5363            decision,
5364            diagnostic.start_index,
5365            format!(
5366                "{:?}:{alts}:{attempt_input}:{result_input}",
5367                diagnostic.kind
5368            ),
5369        );
5370        if !self.reported_prediction_diagnostics.insert(key) {
5371            return;
5372        }
5373        let attempt_diagnostic = diagnostic_for_token(
5374            self.token_at(diagnostic.sll_stop_index),
5375            format!(
5376                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5377            ),
5378        );
5379        self.generated_parser_diagnostics.push(attempt_diagnostic);
5380        let message = match diagnostic.kind {
5381            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5382                // Java's DiagnosticErrorListener is exactOnly by default:
5383                // non-exact ambiguities (default LL mode stopping at the
5384                // first resolvable conflict) report the attempt above but
5385                // suppress the ambiguity line itself.
5386                if !diagnostic.exact {
5387                    return;
5388                }
5389                format!(
5390                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5391                )
5392            }
5393            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5394                format!(
5395                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5396                )
5397            }
5398        };
5399        let result_diagnostic =
5400            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5401        self.generated_parser_diagnostics.push(result_diagnostic);
5402    }
5403
5404    pub fn la(&self, offset: isize) -> i32 {
5405        self.input.la_token(offset)
5406    }
5407
5408    pub fn consume(&mut self) {
5409        IntStream::consume(&mut self.input);
5410    }
5411
5412    /// Sets a generated integer member value used by target-template tests.
5413    pub fn set_int_member(&mut self, member: usize, value: i64) {
5414        self.int_members.insert(member, value);
5415    }
5416
5417    /// Reads a generated integer member value.
5418    pub fn int_member(&self, member: usize) -> Option<i64> {
5419        self.int_members.get(&member).copied()
5420    }
5421
5422    /// Captures generated integer members before speculative generated parser
5423    /// execution.
5424    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
5425        self.int_members.clone()
5426    }
5427
5428    /// Restores generated integer members after generated parser fallback.
5429    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
5430        self.int_members = members;
5431    }
5432
5433    /// Adds `delta` to a generated integer member and returns the new value.
5434    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5435        let value = self.int_members.entry(member).or_default();
5436        *value += delta;
5437        *value
5438    }
5439
5440    fn token_type_for_id(&self, id: TokenId) -> i32 {
5441        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5442    }
5443
5444    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5445        if self.build_parse_trees {
5446            self.tree.terminal(id)
5447        } else {
5448            NodeId::placeholder()
5449        }
5450    }
5451
5452    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5453        if self.build_parse_trees {
5454            self.tree.error(id)
5455        } else {
5456            NodeId::placeholder()
5457        }
5458    }
5459
5460    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5461        context.set_start_id(id);
5462    }
5463
5464    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5465        context.set_stop_id(id);
5466    }
5467
5468    fn insert_synthetic_token(
5469        &mut self,
5470        token_type: i32,
5471        text: String,
5472        line: usize,
5473        column: usize,
5474    ) -> Result<TokenId, AntlrError> {
5475        self.input
5476            .insert(
5477                TokenSpec::explicit(token_type, text)
5478                    .with_span(usize::MAX, usize::MAX)
5479                    .with_byte_span(0, 0)
5480                    .with_position(line, column),
5481            )
5482            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5483    }
5484
5485    /// Matches and consumes the current token when it has the expected token
5486    /// type.
5487    ///
5488    /// On success the consumed token is wrapped as a terminal parse-tree node.
5489    /// On mismatch the error carries vocabulary display names so diagnostics are
5490    /// stable across literal and symbolic token naming.
5491    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5492        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5493            line: 0,
5494            column: 0,
5495            message: "missing current token".to_owned(),
5496            offending: None,
5497        })?;
5498        let current_type = self.token_type_for_id(current);
5499        if current_type == token_type {
5500            self.reset_generated_recovery_state();
5501            self.consume();
5502            Ok(self.terminal_tree(current))
5503        } else {
5504            Err(AntlrError::MismatchedInput {
5505                expected: self.vocabulary().display_name(token_type),
5506                found: self.vocabulary().display_name(current_type),
5507            })
5508        }
5509    }
5510
5511    /// Matches a token from generated recursive-descent code, including ANTLR's
5512    /// single-token insertion recovery when the active rule context can legally
5513    /// continue at the current input symbol.
5514    pub fn match_token_recovering(
5515        &mut self,
5516        token_type: i32,
5517        follow_state: usize,
5518        atn: &Atn,
5519    ) -> Result<GeneratedMatch, AntlrError> {
5520        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5521            line: 0,
5522            column: 0,
5523            message: "missing current token".to_owned(),
5524            offending: None,
5525        })?;
5526        let current_type = self.token_type_for_id(current);
5527        if current_type == token_type {
5528            self.generated_sync_expected = None;
5529            self.reset_generated_recovery_state();
5530            let consumed_eof = current_type == TOKEN_EOF;
5531            self.consume();
5532            return Ok(GeneratedMatch {
5533                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5534                consumed_eof,
5535            });
5536        }
5537        let mut expected_symbols = BTreeSet::new();
5538        expected_symbols.insert(token_type);
5539        self.recover_generated_match(
5540            current,
5541            GeneratedExpectedSymbols::Tree(&expected_symbols),
5542            follow_state,
5543            atn,
5544            |symbol| symbol == token_type,
5545        )
5546    }
5547
5548    pub fn match_set_recovering(
5549        &mut self,
5550        intervals: &[(i32, i32)],
5551        follow_state: usize,
5552        atn: &Atn,
5553    ) -> Result<GeneratedMatch, AntlrError> {
5554        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5555            line: 0,
5556            column: 0,
5557            message: "missing current token".to_owned(),
5558            offending: None,
5559        })?;
5560        let current_type = self.token_type_for_id(current);
5561        if interval_set_contains(intervals, current_type) {
5562            self.generated_sync_expected = None;
5563            self.reset_generated_recovery_state();
5564            let consumed_eof = current_type == TOKEN_EOF;
5565            self.consume();
5566            return Ok(GeneratedMatch {
5567                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5568                consumed_eof,
5569            });
5570        }
5571        let expected_symbols = interval_symbols(intervals);
5572        self.recover_generated_match(
5573            current,
5574            GeneratedExpectedSymbols::Tree(&expected_symbols),
5575            follow_state,
5576            atn,
5577            |symbol| interval_set_contains(intervals, symbol),
5578        )
5579    }
5580
5581    pub fn match_token_set_recovering(
5582        &mut self,
5583        set: ParserIntervalSet<'_>,
5584        follow_state: usize,
5585        atn: &Atn,
5586    ) -> Result<GeneratedMatch, AntlrError> {
5587        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5588            line: 0,
5589            column: 0,
5590            message: "missing current token".to_owned(),
5591            offending: None,
5592        })?;
5593        let current_type = self.token_type_for_id(current);
5594        if set.contains(current_type) {
5595            self.generated_sync_expected = None;
5596            self.reset_generated_recovery_state();
5597            let consumed_eof = current_type == TOKEN_EOF;
5598            self.consume();
5599            return Ok(GeneratedMatch {
5600                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5601                consumed_eof,
5602            });
5603        }
5604        self.recover_generated_match(
5605            current,
5606            GeneratedExpectedSymbols::TokenSet(set),
5607            follow_state,
5608            atn,
5609            |symbol| set.contains(symbol),
5610        )
5611    }
5612
5613    pub fn match_not_set_recovering(
5614        &mut self,
5615        intervals: &[(i32, i32)],
5616        min_vocabulary: i32,
5617        max_vocabulary: i32,
5618        follow_state: usize,
5619        atn: &Atn,
5620    ) -> Result<GeneratedMatch, AntlrError> {
5621        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5622            line: 0,
5623            column: 0,
5624            message: "missing current token".to_owned(),
5625            offending: None,
5626        })?;
5627        let current_type = self.token_type_for_id(current);
5628        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5629            && !interval_set_contains(intervals, current_type)
5630        {
5631            self.generated_sync_expected = None;
5632            self.reset_generated_recovery_state();
5633            let consumed_eof = current_type == TOKEN_EOF;
5634            self.consume();
5635            return Ok(GeneratedMatch {
5636                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5637                consumed_eof,
5638            });
5639        }
5640        let expected_symbols =
5641            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5642        self.recover_generated_match(
5643            current,
5644            GeneratedExpectedSymbols::Tree(&expected_symbols),
5645            follow_state,
5646            atn,
5647            |symbol| {
5648                (min_vocabulary..=max_vocabulary).contains(&symbol)
5649                    && !interval_set_contains(intervals, symbol)
5650            },
5651        )
5652    }
5653
5654    pub fn match_not_token_set_recovering(
5655        &mut self,
5656        set: ParserIntervalSet<'_>,
5657        min_vocabulary: i32,
5658        max_vocabulary: i32,
5659        follow_state: usize,
5660        atn: &Atn,
5661    ) -> Result<GeneratedMatch, AntlrError> {
5662        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5663            line: 0,
5664            column: 0,
5665            message: "missing current token".to_owned(),
5666            offending: None,
5667        })?;
5668        let current_type = self.token_type_for_id(current);
5669        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
5670        {
5671            self.generated_sync_expected = None;
5672            self.reset_generated_recovery_state();
5673            let consumed_eof = current_type == TOKEN_EOF;
5674            self.consume();
5675            return Ok(GeneratedMatch {
5676                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5677                consumed_eof,
5678            });
5679        }
5680        self.recover_generated_match(
5681            current,
5682            GeneratedExpectedSymbols::TokenSetComplement {
5683                set,
5684                min_vocabulary,
5685                max_vocabulary,
5686            },
5687            follow_state,
5688            atn,
5689            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
5690        )
5691    }
5692
5693    fn recover_generated_match(
5694        &mut self,
5695        current: TokenId,
5696        expected_symbols: GeneratedExpectedSymbols<'_>,
5697        follow_state: usize,
5698        atn: &Atn,
5699        matches: impl Fn(i32) -> bool,
5700    ) -> Result<GeneratedMatch, AntlrError> {
5701        let expected_display = expected_symbols.display(self.vocabulary());
5702        let (current_type, current_line, current_column, current_display) = {
5703            let token = self
5704                .input
5705                .token_view(current)
5706                .expect("current token ID should be valid");
5707            (
5708                token.token_type(),
5709                token.line(),
5710                token.column(),
5711                token_input_display(&token),
5712            )
5713        };
5714        if self.bail_on_error {
5715            return Err(AntlrError::ParserError {
5716                line: current_line,
5717                column: current_column,
5718                message: format!("mismatched input {current_display} expecting {expected_display}"),
5719                offending: Some(current),
5720            });
5721        }
5722        if current_type != TOKEN_EOF
5723            && let Some(next) = self.input.lt_id(2)
5724            && matches(self.token_type_for_id(next))
5725        {
5726            let message =
5727                format!("extraneous input {current_display} expecting {expected_display}");
5728            self.push_generated_parser_diagnostic(ParserDiagnostic {
5729                line: current_line,
5730                column: current_column,
5731                message,
5732                offending: Some(current),
5733            });
5734            self.record_syntax_errors(1);
5735            self.generated_sync_expected = None;
5736            // Single-token deletion: skip `current`, then accept `next`. The
5737            // accepted token can be EOF only if it is a real EOF terminal.
5738            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5739            self.consume();
5740            self.consume();
5741            self.reset_generated_recovery_state();
5742            return Ok(GeneratedMatch {
5743                children: GeneratedMatchChildren::Many(vec![
5744                    self.error_tree(current),
5745                    self.terminal_tree(next),
5746                ]),
5747                consumed_eof,
5748            });
5749        }
5750        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5751        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5752        // *after* the current element can consume the current symbol. At EOF that
5753        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5754        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5755        // when EOF merely leaks in from the empty enclosing context (as in
5756        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5757        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5758        // so consult the follow state's OWN expected set for the explicit case.
5759        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5760            && self
5761                .cached_state_expected_symbols(atn, follow_state)
5762                .contains(&TOKEN_EOF);
5763        if follow_symbols.contains(&current_type)
5764            && (current_type != TOKEN_EOF
5765                || self.rule_context_stack.len() > 1
5766                || expected_symbols.is_empty()
5767                || follow_explicitly_expects_eof)
5768        {
5769            let message = format!("missing {expected_display} at {current_display}");
5770            self.push_generated_parser_diagnostic(ParserDiagnostic {
5771                line: current_line,
5772                column: current_column,
5773                message,
5774                offending: Some(current),
5775            });
5776            self.record_syntax_errors(1);
5777            self.generated_sync_expected = None;
5778            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
5779            let missing_display = expected_symbol_display(token_type, self.vocabulary());
5780            let token = self.insert_synthetic_token(
5781                token_type,
5782                format!("<missing {missing_display}>"),
5783                current_line,
5784                current_column,
5785            )?;
5786            // Single-token insertion synthesizes a missing token and consumes
5787            // nothing, so no EOF terminal is consumed even when the lookahead is
5788            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5789            // from recording EOF as the rule stop on this recovery path.
5790            return Ok(GeneratedMatch {
5791                children: GeneratedMatchChildren::One(self.error_tree(token)),
5792                consumed_eof: false,
5793            });
5794        }
5795        let mismatch_expected_display = self
5796            .generated_sync_expected
5797            .take()
5798            .map_or(expected_display, |symbols| {
5799                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
5800            });
5801        Err(AntlrError::ParserError {
5802            line: current_line,
5803            column: current_column,
5804            message: format!(
5805                "mismatched input {current_display} expecting {mismatch_expected_display}"
5806            ),
5807            offending: Some(current),
5808        })
5809    }
5810
5811    fn generated_recovery_follow_symbols(
5812        &mut self,
5813        atn: &Atn,
5814        follow_state: usize,
5815    ) -> BTreeSet<i32> {
5816        let mut follow = self
5817            .cached_state_expected_symbols(atn, follow_state)
5818            .as_ref()
5819            .clone();
5820        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5821            follow.extend(self.context_expected_symbols(atn));
5822        }
5823        follow
5824    }
5825
5826    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5827        self.match_token(TOKEN_EOF)
5828    }
5829
5830    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5831        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5832    }
5833
5834    pub fn match_not_set(
5835        &mut self,
5836        intervals: &[(i32, i32)],
5837        min_vocabulary: i32,
5838        max_vocabulary: i32,
5839    ) -> Result<ParseTree, AntlrError> {
5840        self.match_interval_condition(intervals, |symbol| {
5841            (min_vocabulary..=max_vocabulary).contains(&symbol)
5842                && !interval_set_contains(intervals, symbol)
5843        })
5844    }
5845
5846    fn match_interval_condition(
5847        &mut self,
5848        intervals: &[(i32, i32)],
5849        matches: impl FnOnce(i32) -> bool,
5850    ) -> Result<ParseTree, AntlrError> {
5851        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5852            line: 0,
5853            column: 0,
5854            message: "missing current token".to_owned(),
5855            offending: None,
5856        })?;
5857        let current_type = self.token_type_for_id(current);
5858        if matches(current_type) {
5859            self.reset_generated_recovery_state();
5860            self.consume();
5861            Ok(self.terminal_tree(current))
5862        } else {
5863            Err(AntlrError::MismatchedInput {
5864                expected: self.interval_display(intervals),
5865                found: self.vocabulary().display_name(current_type),
5866            })
5867        }
5868    }
5869
5870    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5871        let values = intervals
5872            .iter()
5873            .map(|(start, stop)| {
5874                if start == stop {
5875                    self.vocabulary().display_name(*start)
5876                } else {
5877                    format!(
5878                        "{}..{}",
5879                        self.vocabulary().display_name(*start),
5880                        self.vocabulary().display_name(*stop)
5881                    )
5882                }
5883            })
5884            .collect::<Vec<_>>()
5885            .join(", ");
5886        format!("{{{values}}}")
5887    }
5888
5889    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5890        if self.build_parse_trees {
5891            self.tree.finish_rule(context)
5892        } else {
5893            NodeId::placeholder()
5894        }
5895    }
5896
5897    /// Reports whether the generated rule dispatch should sample native stack
5898    /// capacity before descending into the next rule body.
5899    ///
5900    /// Generated recursive-descent methods otherwise map unbounded grammar
5901    /// nesting straight onto native call depth; sampling every
5902    /// [`GENERATED_RULE_STACK_CHECK_INTERVAL`] rule-context frames keeps the
5903    /// hot path free of per-call probes while guaranteeing a check runs before
5904    /// the red zone can be crossed.
5905    #[must_use]
5906    pub const fn generated_rule_stack_check_due(&self) -> bool {
5907        self.rule_context_stack
5908            .len()
5909            .is_multiple_of(GENERATED_RULE_STACK_CHECK_INTERVAL)
5910    }
5911
5912    /// Returns the positioned error to abort with when the configured
5913    /// rule-nesting depth cap would be exceeded by one more level, or `None`
5914    /// to keep parsing.
5915    ///
5916    /// Generated rule dispatch calls this before deepening — ahead of the
5917    /// rule-frame push at the dispatch boundary and ahead of each
5918    /// left-recursive expansion — letting callers parsing untrusted input
5919    /// bound CPU and tree memory ([`Parser::set_max_rule_depth`]). The
5920    /// inline fast path is one `Option` check when no cap is set (the
5921    /// default) and one addition plus compare when one is; only an actual
5922    /// violation leaves the inline path.
5923    ///
5924    /// The violation is sticky: rule-level recovery absorbs the returned
5925    /// error like any other rule failure and would otherwise keep spending
5926    /// the very resources the cap exists to bound, so every check after the
5927    /// first violation fails until [`Self::take_rule_depth_error`] drains it
5928    /// at the top-level entry.
5929    #[inline]
5930    pub fn rule_depth_cap_violation(&mut self) -> Option<AntlrError> {
5931        let max = self.max_rule_depth?;
5932        // Left-recursive operator iterations deepen the tree without pushing
5933        // a rule frame, so they count alongside the rule-context stack.
5934        if self.rule_depth_error.is_none()
5935            && self.rule_context_stack.len() + self.recursion_expansions < max
5936        {
5937            return None;
5938        }
5939        Some(self.rule_depth_cap_violation_cold(max))
5940    }
5941
5942    #[cold]
5943    fn rule_depth_cap_violation_cold(&mut self, max: usize) -> AntlrError {
5944        if let Some(error) = &self.rule_depth_error {
5945            return error.clone();
5946        }
5947        let current = self.input.lt(1);
5948        let (line, column) = current
5949            .as_ref()
5950            .map_or((0, 0), |token| (token.line(), token.column()));
5951        let error = AntlrError::ParserError {
5952            line,
5953            column,
5954            message: format!("rule nesting depth limit of {max} exceeded"),
5955            offending: current.as_ref().map(Token::token_id),
5956        };
5957        self.rule_depth_error = Some(error.clone());
5958        error
5959    }
5960
5961    /// Drains the sticky depth-cap violation recorded by
5962    /// [`Self::rule_depth_cap_violation`], if any.
5963    ///
5964    /// Generated top-level rule entries call this after recognition so a
5965    /// recovered parse that crossed the cap still fails, and so a reused
5966    /// parser starts its next parse clean.
5967    pub const fn take_rule_depth_error(&mut self) -> Option<AntlrError> {
5968        self.rule_depth_error.take()
5969    }
5970
5971    /// Reports whether a rule-nesting depth cap is configured.
5972    ///
5973    /// Generated dispatch consults this when selecting between the guarded
5974    /// recursive-descent body and the ATN-preferred interpreted fast path:
5975    /// only the generated body enforces the cap, so a configured bound
5976    /// overrides the performance preference.
5977    #[must_use]
5978    pub const fn has_rule_depth_cap(&self) -> bool {
5979        self.max_rule_depth.is_some()
5980    }
5981
5982    /// Registers a listener for committed rule enter/exit events during
5983    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
5984    /// the delivery contract.
5985    pub fn add_parse_listener<L>(&mut self, listener: L)
5986    where
5987        L: ParseListener + 'static,
5988    {
5989        self.parse_listeners
5990            .push(ParseListenerSlot(Box::new(listener)));
5991    }
5992
5993    /// Removes every registered parse listener and returns them, dropping any
5994    /// sticky abort a removed listener had requested.
5995    ///
5996    /// Returning the boxed listeners gives callers back the state they
5997    /// accumulated (depth counters, collected events) without threading
5998    /// shared handles through the listener.
5999    pub fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
6000        self.parse_listener_abort = None;
6001        self.parse_listeners.drain(..).map(|slot| slot.0).collect()
6002    }
6003
6004    /// Reports whether any parse listener is registered.
6005    ///
6006    /// Generated dispatch consults this alongside [`Self::has_rule_depth_cap`]
6007    /// when choosing between the generated body (which fires events) and the
6008    /// ATN-preferred interpreted fast path (which does not).
6009    #[must_use]
6010    pub const fn has_parse_listeners(&self) -> bool {
6011        !self.parse_listeners.is_empty()
6012    }
6013
6014    /// Fires `enter_every_rule` on registered parse listeners, returning the
6015    /// abort error if any listener requested one.
6016    ///
6017    /// Generated rule dispatch calls this after the depth-cap probe and
6018    /// before the rule body runs; the generated left-recursive loop calls it
6019    /// once per operator expansion, mirroring upstream ANTLR's simulated
6020    /// rule-entry event for `pushNewRecursionContext`. A listener abort is
6021    /// sticky exactly like a depth-cap violation: rule-level recovery absorbs
6022    /// the returned error, so the flag holds until the top-level entry drains
6023    /// it via [`Self::take_parse_listener_abort`] and fails the parse.
6024    pub fn parse_listener_enter_rule(&mut self, rule_index: usize) -> Option<AntlrError> {
6025        if self.parse_listeners.is_empty() {
6026            return None;
6027        }
6028        self.parse_listener_enter_rule_dispatch(rule_index)
6029    }
6030
6031    fn parse_listener_enter_rule_dispatch(&mut self, rule_index: usize) -> Option<AntlrError> {
6032        if let Some(error) = &self.parse_listener_abort {
6033            return Some(error.clone());
6034        }
6035        let event = EnterRuleEvent {
6036            rule_index,
6037            current: self.input.lt(1),
6038        };
6039        // Split borrows: the token view borrows the input while listeners
6040        // need `&mut`, so listeners are taken out for the dispatch. Listener
6041        // methods have no parser access and cannot observe the absence.
6042        let mut listeners = std::mem::take(&mut self.parse_listeners);
6043        let mut abort = None;
6044        for slot in &mut listeners {
6045            if let Err(error) = slot.0.enter_every_rule(&event) {
6046                abort = Some(error);
6047                break;
6048            }
6049        }
6050        self.parse_listeners = listeners;
6051        if let Some(error) = abort {
6052            self.parse_listener_abort = Some(error.clone());
6053            return Some(error);
6054        }
6055        None
6056    }
6057
6058    /// Fires `exit_every_rule` on registered parse listeners.
6059    ///
6060    /// Generated rule bodies call this on every exit path — success and
6061    /// recovery alike — keeping enter/exit pairs balanced, and the generated
6062    /// left-recursive loop calls it once per operator expansion when the rule
6063    /// finishes unrolling.
6064    pub fn parse_listener_exit_rule(&mut self, rule_index: usize) {
6065        if self.parse_listeners.is_empty() {
6066            return;
6067        }
6068        // Reverse registration order, matching upstream ANTLR
6069        // (`Parser.triggerExitRuleEvent` walks listeners back to front).
6070        for slot in self.parse_listeners.iter_mut().rev() {
6071            slot.0.exit_every_rule(rule_index);
6072        }
6073    }
6074
6075    /// Drains the sticky parse-listener abort recorded by
6076    /// [`Self::parse_listener_enter_rule`], if any.
6077    ///
6078    /// Generated top-level rule entries call this after recognition so an
6079    /// aborted parse fails even when recovery produced a tree, and so a
6080    /// reused parser starts its next parse clean.
6081    pub const fn take_parse_listener_abort(&mut self) -> Option<AntlrError> {
6082        self.parse_listener_abort.take()
6083    }
6084
6085    /// Drains every sticky parse abort — the depth-cap violation and the
6086    /// parse-listener abort — returning the depth error preferentially.
6087    ///
6088    /// Generated top-level rule entries call this on both exit paths: the
6089    /// recorded abort wins over errors derived from it (recovery may have
6090    /// absorbed the aborted rule and failed differently later), a recovered
6091    /// `Ok` tree still fails when an abort was recorded, and draining leaves
6092    /// the instance clean for the next entry-rule call.
6093    pub fn take_parse_abort(&mut self) -> Option<AntlrError> {
6094        if let Some(error) = self.rule_depth_error.take() {
6095            self.parse_listener_abort = None;
6096            return Some(error);
6097        }
6098        self.parse_listener_abort.take()
6099    }
6100
6101    /// Enters a generated parser rule and returns the context object the
6102    /// generated method should populate.
6103    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
6104        self.set_state(state);
6105        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
6106        self.rule_context_stack.push(RuleContextFrame {
6107            rule_index,
6108            invoking_state,
6109        });
6110        self.advance_rule_context_version();
6111        let start_index = self.current_visible_index();
6112        let mut context = ParserRuleContext::new(rule_index, invoking_state);
6113        if let Some(token) = self.token_id_at(start_index) {
6114            self.set_context_start(&mut context, token);
6115        }
6116        context
6117    }
6118
6119    /// Records the ATN source state for the next generated rule invocation.
6120    ///
6121    /// ANTLR's full-context prediction reconstructs caller follow states from
6122    /// each active rule context's invoking state. Generated Rust rule methods are
6123    /// plain functions, so the caller supplies that ATN state just before making a
6124    /// rule call; `enter_rule` consumes it when the callee starts.
6125    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
6126        let marker = self.pending_invoking_states.len();
6127        self.pending_invoking_states.push(invoking_state);
6128        marker
6129    }
6130
6131    /// Discards an invoking-state marker if the callee did not consume it.
6132    pub fn discard_invoking_state(&mut self, marker: usize) {
6133        self.pending_invoking_states.truncate(marker);
6134    }
6135
6136    /// Exits the current generated parser rule.
6137    pub fn exit_rule(&mut self) {
6138        self.rule_context_stack.pop();
6139        self.advance_rule_context_version();
6140    }
6141
6142    /// Returns caller follow states for interning in a parser ATN simulator's
6143    /// prediction store. States are yielded outermost to innermost.
6144    pub fn prediction_context_return_states<'a>(
6145        &'a self,
6146        atn: &'a Atn,
6147    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
6148        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
6149            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
6150                return None;
6151            };
6152            let Some(Transition::Rule { follow_state, .. }) = atn
6153                .state(state_number)
6154                .and_then(|state| state.transitions().first())
6155                .map(ParserTransition::data)
6156            else {
6157                return None;
6158            };
6159            Some(follow_state)
6160        })
6161    }
6162
6163    /// Returns a generation that changes whenever the active rule stack changes.
6164    ///
6165    /// A parser ATN simulator uses this to reuse an interned outer prediction
6166    /// context while generated predictions remain in the same rule context.
6167    pub const fn rule_context_version(&self) -> usize {
6168        self.rule_context_version
6169    }
6170
6171    const fn advance_rule_context_version(&mut self) {
6172        self.rule_context_version = self.rule_context_version.wrapping_add(1);
6173    }
6174
6175    /// Adds a generated parser child only when parse-tree construction is
6176    /// enabled. The match is recorded on the context either way (via `add_child`,
6177    /// or `note_matched_child` when trees are off) so generated recovery can tell
6178    /// whether the rule has matched anything yet without depending on `children`.
6179    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
6180        if self.build_parse_trees {
6181            self.tree.add_child(context, child);
6182        } else {
6183            context.note_matched_child();
6184        }
6185    }
6186
6187    fn release_tree_scratch_if_idle(&mut self) {
6188        if self.rule_context_stack.is_empty() {
6189            self.tree.release_scratch();
6190        }
6191    }
6192
6193    /// Finishes a generated parser rule and returns its parse-tree node.
6194    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
6195        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6196        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6197            self.set_context_stop(&mut context, token);
6198        }
6199        let node = self.rule_node(context);
6200        self.exit_rule();
6201        self.release_tree_scratch_if_idle();
6202        node
6203    }
6204
6205    /// Recovers a generated rule catch block after a committed mismatch.
6206    ///
6207    /// ANTLR's generated parsers catch recognition errors inside each rule,
6208    /// report the original error, then consume unexpected tokens until the
6209    /// caller's recovery set can resume. Tokens consumed during recovery become
6210    /// error nodes in the current rule context.
6211    pub fn recover_generated_rule(
6212        &mut self,
6213        context: &mut ParserRuleContext,
6214        atn: &Atn,
6215        error: AntlrError,
6216    ) {
6217        let diagnostic = self.generated_rule_error_diagnostic(error);
6218        self.push_generated_parser_diagnostic(diagnostic);
6219        self.generated_sync_expected = None;
6220        let error_index = self.input.index();
6221        let error_state = self.data.state();
6222        // Match ANTLR's lastErrorIndex/lastErrorStates failsafe: a recovery
6223        // token can also be in the caller's follow set, leaving the cursor
6224        // unchanged and allowing generated outer decisions to revisit the same
6225        // failed state forever.
6226        if self.generated_recovery_error_index == Some(error_index)
6227            && self.generated_recovery_error_states.contains(&error_state)
6228            && self.la(1) != TOKEN_EOF
6229            && let Some(token) = self.input.lt_id(1)
6230        {
6231            self.consume();
6232            let child = self.error_tree(token);
6233            self.add_parse_child(context, child);
6234        }
6235        let recovery_index = self.input.index();
6236        if self.generated_recovery_error_index != Some(recovery_index) {
6237            self.generated_recovery_error_index = Some(recovery_index);
6238            self.generated_recovery_error_states.clear();
6239        }
6240        self.generated_recovery_error_states.insert(error_state);
6241        let recovery_symbols = self.context_expected_symbols(atn);
6242        loop {
6243            let symbol = self.la(1);
6244            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
6245                break;
6246            }
6247            let Some(token) = self.input.lt_id(1) else {
6248                break;
6249            };
6250            self.consume();
6251            let child = self.error_tree(token);
6252            self.add_parse_child(context, child);
6253        }
6254        self.record_syntax_errors(1);
6255    }
6256
6257    fn reset_generated_recovery_state(&mut self) {
6258        if self.generated_recovery_error_index.is_some() {
6259            self.generated_recovery_error_index = None;
6260            self.generated_recovery_error_states.clear();
6261        }
6262    }
6263
6264    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
6265        if self
6266            .generated_parser_diagnostics
6267            .iter()
6268            .any(|existing| existing == &diagnostic)
6269        {
6270            return;
6271        }
6272        self.generated_parser_diagnostics.push(diagnostic);
6273    }
6274
6275    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
6276        match error {
6277            // The anchor recorded where the error was built wins over the
6278            // current lookahead: prediction restores the cursor, so lt(1)
6279            // here can point at the decision start rather than the error.
6280            AntlrError::ParserError {
6281                line,
6282                column,
6283                message,
6284                offending,
6285            } => ParserDiagnostic {
6286                line,
6287                column,
6288                message,
6289                offending,
6290            },
6291            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
6292                self.input.lt(1),
6293                format!("mismatched input {found} expecting {expected}"),
6294            ),
6295            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
6296                self.input.lt(1),
6297                format!("no viable alternative at input {input}"),
6298            ),
6299            AntlrError::LexerError {
6300                line,
6301                column,
6302                message,
6303            } => ParserDiagnostic {
6304                line,
6305                column,
6306                message,
6307                offending: None,
6308            },
6309            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
6310        }
6311    }
6312
6313    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
6314    pub fn finish_recursion_rule(
6315        &mut self,
6316        mut context: ParserRuleContext,
6317        consumed_eof: bool,
6318    ) -> ParseTree {
6319        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6320        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6321            self.set_context_stop(&mut context, token);
6322        }
6323        let node = self.rule_node(context);
6324        self.unroll_recursion_context();
6325        self.release_tree_scratch_if_idle();
6326        node
6327    }
6328
6329    /// Enters a generated left-recursive rule at `precedence`.
6330    pub fn enter_recursion_rule(
6331        &mut self,
6332        state: isize,
6333        rule_index: usize,
6334        precedence: i32,
6335    ) -> ParserRuleContext {
6336        self.precedence_stack.push(precedence);
6337        self.recursion_expansion_marks
6338            .push(self.recursion_expansions);
6339        self.enter_rule(state, rule_index)
6340    }
6341
6342    /// Replaces the current context while expanding a left-recursive rule.
6343    pub fn push_new_recursion_context(
6344        &mut self,
6345        state: isize,
6346        rule_index: usize,
6347    ) -> ParserRuleContext {
6348        self.set_state(state);
6349        // Counts toward the depth cap: upstream treats this as rule entry
6350        // (`Parser.pushNewRecursionContext` fires `triggerEnterRuleEvent`).
6351        self.recursion_expansions += 1;
6352        ParserRuleContext::new(rule_index, state)
6353    }
6354
6355    /// Wraps the previous left-recursive context before parsing the next
6356    /// recursive operator alternative.
6357    pub fn push_new_recursion_context_with_previous(
6358        &mut self,
6359        state: isize,
6360        rule_index: usize,
6361        current: &mut ParserRuleContext,
6362    ) {
6363        self.set_state(state);
6364        // Counts toward the depth cap: each operator iteration deepens the
6365        // parse tree one level without pushing a rule frame, and upstream
6366        // fires a rule-entry listener event for it. The parse-listener enter
6367        // event for this expansion fires from the generated loop's probe
6368        // just before this call, where a listener abort can propagate.
6369        self.recursion_expansions += 1;
6370        if let Some(stop) = self
6371            .rule_stop_token_index(self.input.index(), false)
6372            .and_then(|index| self.token_id_at(index))
6373        {
6374            self.set_context_stop(current, stop);
6375        }
6376        let invoking_state = current.invoking_state();
6377        let start = current.start_id();
6378        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
6379        if start.is_some() {
6380            replacement.set_start_from_context(current);
6381        }
6382        let previous = std::mem::replace(current, replacement);
6383        if self.build_parse_trees {
6384            let previous = self.rule_node(previous);
6385            self.tree.add_child(current, previous);
6386        }
6387    }
6388
6389    /// Leaves a generated left-recursive rule.
6390    pub fn unroll_recursion_context(&mut self) {
6391        if self.precedence_stack.len() > 1 {
6392            self.precedence_stack.pop();
6393        }
6394        // Parse-listener exits for expansions fire inside the generated
6395        // operator loop (top of each pass, upstream's `recRuleSetPrevCtx`),
6396        // and the dispatch wrapper's exit covers the final live context —
6397        // upstream's `unrollRecursionContexts` walks exactly one link, so no
6398        // batched exits happen here. Only the depth-cap accounting rewinds.
6399        if let Some(mark) = self.recursion_expansion_marks.pop() {
6400            self.recursion_expansions = mark;
6401        }
6402        self.exit_rule();
6403    }
6404
6405    /// Predicts a generated left-recursive loop from one-token lookahead.
6406    ///
6407    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
6408    /// `None` means caller overlap, a dangerous multi-token prefix, or an
6409    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
6410    /// prediction (which includes the exit alt and precedence filtering).
6411    ///
6412    /// Single-token operators and multi-token prefixes that do not shadow a
6413    /// lower-precedence single-token operator keep the one-token enter fast path.
6414    ///
6415    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
6416    /// operator must not force enter; the adaptive decision may need to select
6417    /// the loop exit instead.
6418    pub fn left_recursive_loop_enter_prediction(
6419        &mut self,
6420        atn: &Atn,
6421        state_number: usize,
6422        precedence: i32,
6423    ) -> Option<bool> {
6424        let symbol = self.la(1);
6425        if symbol == TOKEN_EOF {
6426            return Some(false);
6427        }
6428        let operator_lookahead =
6429            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
6430        let can_single = operator_lookahead.single_token.contains(symbol);
6431        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
6432        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
6433        if !can_single && !can_multi && !can_predicate {
6434            return Some(false);
6435        }
6436        if can_predicate && !can_single {
6437            return None;
6438        }
6439        // Multi-token-only at this precedence, but the same symbol is a
6440        // single-token operator at precedence 0: defer so exit can win when the
6441        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
6442        if !can_single && can_multi && precedence > 0 {
6443            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
6444            if baseline.single_token.contains(symbol) {
6445                return None;
6446            }
6447        }
6448        let atn_key = SharedAtnCacheKey::for_atn(atn);
6449        let cached_overlap = self
6450            .left_recursive_caller_overlap_cache
6451            .iter()
6452            .flatten()
6453            .find(|entry| {
6454                entry.atn_key == atn_key
6455                    && entry.state_number == state_number
6456                    && entry.symbol == symbol
6457                    && entry.context_version == self.rule_context_version
6458            })
6459            .map(|entry| entry.overlaps);
6460        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
6461            let overlaps = caller_context_can_match_symbol_before_state(
6462                atn,
6463                self.prediction_context_return_states(atn),
6464                state_number,
6465                symbol,
6466            );
6467            if let Some(slot) = self
6468                .left_recursive_caller_overlap_cache
6469                .iter_mut()
6470                .find(|slot| slot.is_none())
6471            {
6472                *slot = Some(LeftRecursiveCallerOverlap {
6473                    atn_key,
6474                    state_number,
6475                    symbol,
6476                    context_version: self.rule_context_version,
6477                    overlaps,
6478                });
6479            }
6480            overlaps
6481        });
6482        if caller_overlaps {
6483            return None;
6484        }
6485        Some(true)
6486    }
6487
6488    fn cached_left_recursive_operator_lookahead(
6489        atn: &Atn,
6490        state_number: usize,
6491        precedence: i32,
6492    ) -> Rc<LeftRecursiveOperatorLookahead> {
6493        with_shared_atn_caches(atn, |cache| {
6494            let key = (state_number, precedence);
6495            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
6496                return Rc::clone(cached);
6497            }
6498            let lookahead = Rc::new(left_recursive_operator_lookahead(
6499                atn,
6500                state_number,
6501                precedence,
6502            ));
6503            cache
6504                .left_recursive_operator_lookahead
6505                .insert(key, Rc::clone(&lookahead));
6506            lookahead
6507        })
6508    }
6509
6510    /// Checks whether a generated left-recursive loop can unambiguously enter
6511    /// its operator alternative from one-token lookahead.
6512    pub fn left_recursive_loop_enter_matches(
6513        &mut self,
6514        atn: &Atn,
6515        state_number: usize,
6516        precedence: i32,
6517    ) -> bool {
6518        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
6519    }
6520
6521    /// Implements generated `precpred(_ctx, k)` checks.
6522    pub fn precpred(&self, precedence: i32) -> bool {
6523        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
6524    }
6525
6526    /// Evaluates a generated parser semantic predicate at the current input
6527    /// position.
6528    pub fn parser_semantic_predicate_matches(
6529        &mut self,
6530        predicates: &[(usize, usize, ParserPredicate)],
6531        rule_index: usize,
6532        pred_index: usize,
6533    ) -> bool {
6534        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
6535    }
6536
6537    /// Evaluates a generated parser semantic predicate with the current integer
6538    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
6539    pub fn parser_semantic_predicate_matches_with_local(
6540        &mut self,
6541        predicates: &[(usize, usize, ParserPredicate)],
6542        rule_index: usize,
6543        pred_index: usize,
6544        local_int_arg: i32,
6545    ) -> bool {
6546        self.parser_semantic_predicate_matches_inner(
6547            predicates,
6548            rule_index,
6549            pred_index,
6550            Some((rule_index, i64::from(local_int_arg))),
6551        )
6552    }
6553
6554    fn parser_semantic_predicate_matches_inner(
6555        &mut self,
6556        predicates: &[(usize, usize, ParserPredicate)],
6557        rule_index: usize,
6558        pred_index: usize,
6559        local_int_arg: Option<(usize, i64)>,
6560    ) -> bool {
6561        let index = self.input.index();
6562        let member_values = self.int_members.clone();
6563        self.parser_predicate_matches(PredicateEval {
6564            index,
6565            rule_index,
6566            pred_index,
6567            predicates,
6568            semantics: None,
6569            context: None,
6570            local_int_arg,
6571            member_values: &member_values,
6572        })
6573    }
6574
6575    /// Evaluates a generated parser semantic predicate with access to the
6576    /// current generated rule context.
6577    pub fn parser_semantic_predicate_matches_with_context_and_local(
6578        &mut self,
6579        predicates: &[(usize, usize, ParserPredicate)],
6580        rule_index: usize,
6581        pred_index: usize,
6582        context: &ParserRuleContext,
6583        local_int_arg: i32,
6584    ) -> bool {
6585        let index = self.input.index();
6586        let member_values = self.int_members.clone();
6587        self.parser_predicate_matches(PredicateEval {
6588            index,
6589            rule_index,
6590            pred_index,
6591            predicates,
6592            semantics: None,
6593            context: Some(context),
6594            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6595            member_values: &member_values,
6596        })
6597    }
6598
6599    /// Evaluates a generated `SemIR` parser predicate with access to the current
6600    /// generated rule context.
6601    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
6602        &mut self,
6603        semantics: &ParserSemantics,
6604        rule_index: usize,
6605        pred_index: usize,
6606        context: &ParserRuleContext,
6607        local_int_arg: i32,
6608    ) -> bool {
6609        let index = self.input.index();
6610        let member_values = self.int_members.clone();
6611        self.parser_predicate_matches(PredicateEval {
6612            index,
6613            rule_index,
6614            pred_index,
6615            predicates: &[],
6616            semantics: Some(semantics),
6617            context: Some(context),
6618            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6619            member_values: &member_values,
6620        })
6621    }
6622
6623    /// Returns a generated fail-option message for a parser semantic
6624    /// predicate coordinate.
6625    pub fn parser_semantic_predicate_failure_message(
6626        &self,
6627        rule_index: usize,
6628        pred_index: usize,
6629        predicates: &[(usize, usize, ParserPredicate)],
6630    ) -> Option<&'static str> {
6631        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
6632    }
6633
6634    /// Matches any non-EOF token.
6635    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
6636        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6637            line: 0,
6638            column: 0,
6639            message: "missing current token".to_owned(),
6640            offending: None,
6641        })?;
6642        if self.token_type_for_id(current) == TOKEN_EOF {
6643            return Err(AntlrError::MismatchedInput {
6644                expected: "wildcard".to_owned(),
6645                found: self.vocabulary().display_name(TOKEN_EOF),
6646            });
6647        }
6648        self.reset_generated_recovery_state();
6649        self.consume();
6650        Ok(self.terminal_tree(current))
6651    }
6652
6653    /// Generated parser synchronization hook. The current interpreter owns
6654    /// recovery; direct generated methods can call this as a no-op until the
6655    /// generated recovery strategy is expanded.
6656    #[allow(clippy::unnecessary_wraps)]
6657    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
6658        self.set_state(state);
6659        Ok(())
6660    }
6661
6662    /// Synchronizes a generated parser decision against the ATN lookahead set.
6663    ///
6664    /// ANTLR generated parsers call the error strategy before optional and loop
6665    /// decisions. When the current token cannot start any alternative, follow a
6666    /// nullable exit, or be deleted before a later synchronization token, the
6667    /// generated Rust method reports that decision-level mismatch instead of
6668    /// descending into a child rule that cannot start at the current token.
6669    pub fn sync_decision(
6670        &mut self,
6671        atn: &Atn,
6672        state_number: usize,
6673        current_context_empty: bool,
6674        loop_back: bool,
6675    ) -> Result<Vec<ParseTree>, AntlrError> {
6676        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
6677        self.generated_sync_expected = None;
6678        let Some(state) = atn.state(state_number) else {
6679            return Ok(Vec::new());
6680        };
6681        let Some(rule_index) = state.rule_index() else {
6682            return Ok(Vec::new());
6683        };
6684        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
6685            return Ok(Vec::new());
6686        };
6687        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6688        let symbol = self.la(1);
6689        let mut has_expected_symbols = false;
6690        let mut nullable = false;
6691        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
6692        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
6693        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
6694        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
6695        // and worth deleting; an implicit-follow EOF means the loop should simply
6696        // exit and leave the token for the (absent) caller — matching ANTLR, which
6697        // exits the loop via prediction rather than consuming up to a synthetic EOF.
6698        let mut explicit_eof_expected = false;
6699        for transition in &entry.transitions {
6700            if transition.symbols.contains(symbol) {
6701                return Ok(Vec::new());
6702            }
6703            has_expected_symbols |= !transition.symbols.is_empty();
6704            nullable |= transition.nullable;
6705            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
6706        }
6707        // Happy path: a nullable decision exits when the symbol is in the
6708        // rule-stack follow set. Answer the membership question with an
6709        // early-exit walk; the full union below is only needed for the
6710        // mismatch/deletion diagnostics.
6711        if nullable && self.context_expected_contains(atn, symbol) {
6712            return Ok(Vec::new());
6713        }
6714        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
6715        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
6716            return Ok(Vec::new());
6717        }
6718        let mut expected = TokenBitSet::default();
6719        for transition in &entry.transitions {
6720            expected.extend_from(&transition.symbols);
6721        }
6722        if let Some(context_expected) = context_expected {
6723            expected.extend_from(&context_expected);
6724        }
6725        let can_delete_in_place =
6726            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
6727        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
6728        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
6729        // least one iteration) does `consumeUntil` the follow set — multi-token
6730        // deletion, one error per skipped token across iterations; a loop ENTRY
6731        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
6732        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
6733        // unexpected token only when LA(2) is expected, otherwise report a mismatch
6734        // and leave recovery to the rule.
6735        //
6736        // The generated loop always presents the loop-ENTRY state to this method on
6737        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
6738        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
6739        // an iteration has been taken, and true on a `+` loop's first sync since its
6740        // mandatory first element is iteration 1). Treating a loop entry as a
6741        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
6742        // `c`s, which ANTLR rejects with `mismatched input`).
6743        let loop_sync = loop_back;
6744        if symbol != TOKEN_EOF && can_delete_in_place {
6745            let mut cursor = self.input.index();
6746            let mut skipped = Vec::new();
6747            loop {
6748                let current = self.token_type_at(cursor);
6749                if current == TOKEN_EOF {
6750                    break;
6751                }
6752                skipped.push(cursor);
6753                let next = self.consume_index(cursor, current);
6754                if next == cursor {
6755                    break;
6756                }
6757                let next_symbol = self.token_type_at(next);
6758                // Stop (and delete the skipped tokens as error nodes) when the next
6759                // token is a real expected continuation. EOF counts only when it is
6760                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
6761                // genuinely extraneous and the generated EOF match consumes the real
6762                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
6763                // rule-follow) does NOT count — the loop must exit and leave the
6764                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
6765                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
6766                    explicit_eof_expected
6767                } else {
6768                    expected.contains(next_symbol)
6769                };
6770                if next_is_expected_stop {
6771                    let current_token = self.input.lt(1);
6772                    let expected_symbols = expected.to_btree_set();
6773                    let message = format!(
6774                        "extraneous input {} expecting {}",
6775                        current_token
6776                            .as_ref()
6777                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6778                        self.expected_symbols_display(&expected_symbols)
6779                    );
6780                    self.push_generated_parser_diagnostic(diagnostic_for_token(
6781                        current_token,
6782                        message,
6783                    ));
6784                    self.record_syntax_errors(1);
6785                    let mut children = Vec::with_capacity(skipped.len());
6786                    for index in skipped {
6787                        if let Some(token) = self.token_id_at(index) {
6788                            self.consume();
6789                            children.push(self.error_tree(token));
6790                        }
6791                    }
6792                    if !loop_sync {
6793                        self.reset_generated_recovery_state();
6794                    }
6795                    return Ok(children);
6796                }
6797                // A non-loop block entry deletes at most one token (single-token
6798                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6799                // is reported at the first token instead of skipping ahead.
6800                if !loop_sync {
6801                    break;
6802                }
6803                cursor = next;
6804            }
6805        }
6806        if nullable {
6807            self.generated_sync_expected = Some(expected);
6808            return Ok(Vec::new());
6809        }
6810        let current = self.input.lt(1);
6811        let expected_symbols = expected.to_btree_set();
6812        Err(AntlrError::ParserError {
6813            line: current.as_ref().map(Token::line).unwrap_or_default(),
6814            column: current.as_ref().map(Token::column).unwrap_or_default(),
6815            message: format!(
6816                "mismatched input {} expecting {}",
6817                current
6818                    .as_ref()
6819                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6820                self.expected_symbols_display(&expected_symbols)
6821            ),
6822            offending: current.as_ref().map(Token::token_id),
6823        })
6824    }
6825
6826    /// Returns a generated-parser prediction when one token of lookahead
6827    /// uniquely selects an alternative for `state_number`.
6828    ///
6829    /// This mirrors the interpreter's LL(1) commit point and lets generated
6830    /// recursive-descent methods avoid invoking the adaptive simulator for
6831    /// simple optional/block/loop decisions.
6832    pub fn ll1_decision_prediction(
6833        &mut self,
6834        atn: &Atn,
6835        state_number: usize,
6836    ) -> Option<ParserAtnPrediction> {
6837        let state = atn.state(state_number)?;
6838        if state.precedence_rule_decision() {
6839            return None;
6840        }
6841        let rule_stop = state
6842            .rule_index()
6843            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6844        let symbol = self.la(1);
6845        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6846        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6847            alt: alt + 1,
6848            requires_full_context: false,
6849            has_semantic_context: false,
6850            diagnostic: None,
6851        })
6852    }
6853
6854    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6855        let mut expected = BTreeSet::new();
6856        for index in (1..self.rule_context_stack.len()).rev() {
6857            let invoking_state = self.rule_context_stack[index].invoking_state;
6858            let Ok(state_number) = usize::try_from(invoking_state) else {
6859                continue;
6860            };
6861            let Some(Transition::Rule { follow_state, .. }) = atn
6862                .state(state_number)
6863                .and_then(|state| state.transitions().first())
6864                .map(ParserTransition::data)
6865            else {
6866                continue;
6867            };
6868            let return_state = follow_state;
6869            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6870            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
6871                return expected;
6872            }
6873        }
6874        expected.insert(TOKEN_EOF);
6875        expected
6876    }
6877
6878    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6879        let mut expected = TokenBitSet::default();
6880        for index in (1..self.rule_context_stack.len()).rev() {
6881            let invoking_state = self.rule_context_stack[index].invoking_state;
6882            let Ok(state_number) = usize::try_from(invoking_state) else {
6883                continue;
6884            };
6885            let Some(Transition::Rule { follow_state, .. }) = atn
6886                .state(state_number)
6887                .and_then(|state| state.transitions().first())
6888                .map(ParserTransition::data)
6889            else {
6890                continue;
6891            };
6892            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
6893            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6894                return expected;
6895            }
6896        }
6897        expected.insert(TOKEN_EOF);
6898        expected
6899    }
6900
6901    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
6902    /// without materializing the union.
6903    ///
6904    /// This walks the rule-invocation stack directly, innermost frame first —
6905    /// the same frames, in the same order, with the same rule-stop gating as
6906    /// the same outer-context return-state chain used by adaptive prediction.
6907    /// The nullable
6908    /// exit in `sync_decision` asks only this membership question, and on
6909    /// valid input the innermost frame answers it, so the early exit replaces
6910    /// an O(stack-depth) set union per loop/optional exit with one probe.
6911    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
6912        for index in (1..self.rule_context_stack.len()).rev() {
6913            let invoking_state = self.rule_context_stack[index].invoking_state;
6914            let Ok(state_number) = usize::try_from(invoking_state) else {
6915                continue;
6916            };
6917            let Some(Transition::Rule { follow_state, .. }) = atn
6918                .state(state_number)
6919                .and_then(|state| state.transitions().first())
6920                .map(ParserTransition::data)
6921            else {
6922                continue;
6923            };
6924            if self
6925                .cached_state_expected_token_set(atn, follow_state)
6926                .contains(symbol)
6927            {
6928                return true;
6929            }
6930            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
6931                return false;
6932            }
6933        }
6934        symbol == TOKEN_EOF
6935    }
6936
6937    /// Builds a generated no-viable-alternative parser error.
6938    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
6939        let error_index = self.input.index();
6940        self.no_viable_alternative_error_at(start_index, error_index)
6941    }
6942
6943    /// Builds a generated no-viable-alternative parser error at the simulator's
6944    /// failing lookahead index. `adaptive_predict` restores the input cursor
6945    /// before returning, so generated parsers have to pass the recorded index
6946    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
6947    pub fn no_viable_alternative_error_at(
6948        &self,
6949        start_index: usize,
6950        error_index: usize,
6951    ) -> AntlrError {
6952        let diagnostic = self.no_viable_alternative(start_index, error_index);
6953        AntlrError::ParserError {
6954            line: diagnostic.line,
6955            column: diagnostic.column,
6956            message: diagnostic.message,
6957            offending: diagnostic.offending,
6958        }
6959    }
6960
6961    /// Builds a generated failed-predicate parser error.
6962    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
6963        let current = self.input.lt(1);
6964        AntlrError::ParserError {
6965            line: current.as_ref().map(Token::line).unwrap_or_default(),
6966            column: current.as_ref().map(Token::column).unwrap_or_default(),
6967            message: format!("rule failed predicate: {}", message.into()),
6968            offending: current.as_ref().map(Token::token_id),
6969        }
6970    }
6971
6972    /// Builds a generated parser error for a semantic predicate with ANTLR's
6973    /// `<fail='...'>` option.
6974    pub fn failed_predicate_option_error(
6975        &self,
6976        rule_index: usize,
6977        message: impl Into<String>,
6978    ) -> AntlrError {
6979        let current = self.input.lt(1);
6980        let rule_name = self
6981            .rule_names()
6982            .get(rule_index)
6983            .map_or_else(|| rule_index.to_string(), Clone::clone);
6984        AntlrError::ParserError {
6985            line: current.as_ref().map(Token::line).unwrap_or_default(),
6986            column: current.as_ref().map(Token::column).unwrap_or_default(),
6987            message: format!("rule {rule_name} {}", message.into()),
6988            offending: current.as_ref().map(Token::token_id),
6989        }
6990    }
6991
6992    /// Builds a generated parser-action event at the current input position.
6993    pub fn parser_action_at_current(
6994        &mut self,
6995        source_state: usize,
6996        rule_index: usize,
6997        start_index: usize,
6998        consumed_eof: bool,
6999    ) -> ParserAction {
7000        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
7001        ParserAction::new(source_state, rule_index, start_index, stop_index)
7002    }
7003
7004    /// Offers a committed parser action event to the user semantic hook.
7005    ///
7006    /// Generated parsers call this for action source states that were present
7007    /// in the ATN but not translated into a built-in Rust action template.
7008    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
7009        let rule_index = action.rule_index();
7010        let rule_name = self.rule_names().get(rule_index).cloned();
7011        let context = None;
7012        let input = &mut self.input;
7013        let semantic_hooks = &mut self.semantic_hooks;
7014        let member_values = &self.int_members;
7015        let mut ctx = ParserSemCtx {
7016            input,
7017            tree_storage: &self.tree,
7018            rule_index,
7019            coordinate_index: usize::MAX,
7020            rule_name,
7021            context,
7022            tree: Some(tree),
7023            local_int_arg: None,
7024            member_values,
7025            action: Some(action),
7026        };
7027        let handled = semantic_hooks.action(&mut ctx, action);
7028        // This action reached the hook because it had no translated arm. If no
7029        // hook handled it either (`SemanticHooks::action` returns `false`), the
7030        // committed action is silently dropped — record it so the parse entry
7031        // can fail loud under the fail-loud boundary, mirroring unknown
7032        // predicates. `assume-*` policies opt out of the fail-loud recording.
7033        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
7034            let coordinate = (rule_index, action.source_state());
7035            if !self.unhandled_action_hits.contains(&coordinate) {
7036                self.unhandled_action_hits.push(coordinate);
7037            }
7038        }
7039        handled
7040    }
7041
7042    /// Attempts to execute a whole generated rule by committing simulator
7043    /// decisions directly. Unsupported constructs or decisions that need
7044    /// full-context / predicate evaluation restore the input cursor and fall
7045    /// back to [`Self::parse_atn_rule`].
7046    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
7047        &mut self,
7048        atn: &'atn Atn,
7049        simulator: &mut ParserAtnSimulator<'atn>,
7050        rule_index: usize,
7051    ) -> Result<ParseTree, AntlrError> {
7052        let start_index = self.current_visible_index();
7053        self.clear_prediction_diagnostics();
7054        self.reset_per_parse_caches();
7055        self.reset_recognition_arena();
7056        let tree_checkpoint = self.tree.checkpoint();
7057        let mut decision_by_state = vec![None; atn.states().len()];
7058        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
7059            if let Some(slot) = decision_by_state.get_mut(state_number) {
7060                *slot = Some(decision);
7061            }
7062        }
7063
7064        let result = DirectAdaptiveParser {
7065            parser: self,
7066            atn,
7067            simulator,
7068            decision_by_state,
7069            steps: 0,
7070        }
7071        .parse_rule(rule_index, -1, 0);
7072
7073        match result {
7074            Ok(tree) => {
7075                self.report_token_source_errors();
7076                self.release_tree_scratch_if_idle();
7077                Ok(tree)
7078            }
7079            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
7080                let _ = reason;
7081                self.tree.rollback(tree_checkpoint);
7082                self.input.seek(start_index);
7083                self.parse_atn_rule(atn, rule_index)
7084            }
7085        }
7086    }
7087
7088    /// Parses a generated rule by interpreting the parser ATN from the rule's
7089    /// start state to its stop state.
7090    ///
7091    /// The recognizer backtracks across alternatives and loop exits using token
7092    /// stream indices instead of committing to input consumption immediately.
7093    /// Once a viable ATN path is found, the parser commits the accepted token
7094    /// interval and returns a rule node whose children mirror every grammar
7095    /// rule invocation reached on that path, matching ANTLR's parse-tree
7096    /// shape.
7097    pub fn parse_atn_rule(
7098        &mut self,
7099        atn: &Atn,
7100        rule_index: usize,
7101    ) -> Result<ParseTree, AntlrError> {
7102        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
7103    }
7104
7105    /// Parses a generated rule by interpreting the parser ATN with an initial
7106    /// left-recursive precedence threshold.
7107    pub fn parse_atn_rule_with_precedence(
7108        &mut self,
7109        atn: &Atn,
7110        rule_index: usize,
7111        precedence: i32,
7112    ) -> Result<ParseTree, AntlrError> {
7113        self.parse_atn_rule_with_precedence_inner(
7114            atn,
7115            rule_index,
7116            precedence,
7117            None,
7118            AltNumberTracking::default(),
7119        )
7120    }
7121
7122    fn parse_atn_rule_with_precedence_inner(
7123        &mut self,
7124        atn: &Atn,
7125        rule_index: usize,
7126        precedence: i32,
7127        predicate_context: Option<FastPredicateContext<'_>>,
7128        alt_tracking: AltNumberTracking,
7129    ) -> Result<ParseTree, AntlrError> {
7130        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7131            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7132        })?;
7133        let stop_state = atn
7134            .rule_to_stop_state()
7135            .get(rule_index)
7136            .filter(|state| *state != usize::MAX)
7137            .ok_or_else(|| {
7138                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7139            })?;
7140
7141        let start_index = self.current_visible_index();
7142        self.clear_prediction_diagnostics();
7143        self.reset_per_parse_caches();
7144        self.reset_recognition_arena();
7145        let caller_follow_state = self.pending_invoking_follow_state(atn);
7146        self.fast_recovery_enabled = false;
7147        self.fast_token_nodes_enabled = false;
7148        self.fast_track_alt_numbers = alt_tracking.any();
7149        let top_request = FastRecognizeTopRequest {
7150            start_state,
7151            stop_state,
7152            start_index,
7153            precedence,
7154            caller_follow_state,
7155        };
7156        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
7157        self.fast_token_nodes_enabled = self.build_parse_trees;
7158        let needs_tree_retry = matches!(
7159            &first_pass,
7160            Ok((outcome, _, _))
7161                if self.build_parse_trees
7162                    && self
7163                        .recognition_arena
7164                        .sequence_has_left_recursive_boundary(outcome.nodes)
7165        );
7166        let needs_retry = match &first_pass {
7167            // The FIRST-set prefilter trims speculative rule calls that can't
7168            // match the current lookahead — useful for perf on grammars with
7169            // many epsilon-reachable rules, but the trim also bypasses
7170            // single-token insertion / deletion recovery that ANTLR's
7171            // reference parser runs at the child rule's first consuming
7172            // transition. Retry without the prefilter whenever the first pass
7173            // either produced no outcome at all or produced a recovered
7174            // outcome (diagnostics non-empty), since the second pass might
7175            // surface a child-level recovery with cleaner diagnostics or
7176            // closer parity to ANTLR's tree shape. Left-recursive tree
7177            // boundaries also need the token-node pass; otherwise the fold has
7178            // no concrete left operand to wrap into ANTLR's recursive context.
7179            Err(_) => true,
7180            Ok((outcome, _, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
7181        };
7182        let (outcome, _expected, alt_number) = if needs_retry {
7183            self.fast_first_set_prefilter = false;
7184            self.fast_recovery_enabled = false;
7185            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7186            let clean_selected = if needs_tree_retry {
7187                match clean_retry {
7188                    ok @ Ok(_) => ok,
7189                    Err(_) => first_pass,
7190                }
7191            } else {
7192                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
7193            };
7194            let selected = if clean_selected.is_err()
7195                || matches!(&clean_selected, Ok((outcome, _, _)) if !outcome.diagnostics.is_empty())
7196            {
7197                self.fast_recovery_enabled = true;
7198                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7199                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
7200            } else {
7201                clean_selected
7202            };
7203            self.fast_first_set_prefilter = true;
7204            self.fast_recovery_enabled = true;
7205            selected.map_err(|expected| {
7206                if predicate_context.is_some()
7207                    && let Some(error) = self.unknown_semantic_error()
7208                {
7209                    self.report_token_source_errors();
7210                    return error;
7211                }
7212                let error = self.recognition_error(rule_index, start_index, &expected);
7213                self.record_syntax_errors(1);
7214                self.report_token_source_errors();
7215                error
7216            })?
7217        } else {
7218            first_pass.expect("first_pass is Ok in the no-retry branch")
7219        };
7220        if predicate_context.is_some()
7221            && let Some(error) = self.unknown_semantic_error()
7222        {
7223            self.report_token_source_errors();
7224            return Err(error);
7225        }
7226        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7227        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7228        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7229        self.report_token_source_errors();
7230        let mut context = ParserRuleContext::with_child_capacity(
7231            rule_index,
7232            self.state(),
7233            if self.build_parse_trees {
7234                self.recognition_arena.sequence_len(outcome.nodes)
7235            } else {
7236                0
7237            },
7238        );
7239        if alt_tracking.public {
7240            context.set_alt_number(alt_number.max(1));
7241        }
7242        if alt_tracking.context {
7243            context.set_context_alt_number(alt_number);
7244        }
7245        if let Some(token) = self.token_id_at(start_index) {
7246            self.set_context_start(&mut context, token);
7247        }
7248        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
7249        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
7250            self.set_context_stop(&mut context, token);
7251        }
7252        let live_root = if self.build_parse_trees {
7253            self.recognition_arena
7254                .fold_left_recursive_boundaries(outcome.nodes)
7255        } else {
7256            outcome.nodes
7257        };
7258        if self.build_parse_trees {
7259            if self
7260                .recognition_arena
7261                .sequence_has_explicit_token(live_root)
7262            {
7263                let mut cursor = live_root;
7264                while let Some(link) = self.recognition_arena.link(cursor) {
7265                    let child = self.arena_recognized_node_tree(
7266                        link.head,
7267                        alt_tracking.public,
7268                        alt_tracking.context,
7269                    )?;
7270                    self.tree.add_child(&mut context, child);
7271                    cursor = link.tail;
7272                }
7273            } else {
7274                self.add_arena_implicit_token_children(
7275                    &mut context,
7276                    start_index,
7277                    stop_index,
7278                    live_root,
7279                    alt_tracking,
7280                )?;
7281            }
7282        }
7283        self.finish_recognition_arena(live_root, outcome.diagnostics);
7284        self.input.seek(outcome.index);
7285
7286        let tree = self.rule_node(context);
7287        self.release_tree_scratch_if_idle();
7288        Ok(tree)
7289    }
7290
7291    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
7292        let invoking_state = self.pending_invoking_states.last().copied()?;
7293        let state_number = usize::try_from(invoking_state).ok()?;
7294        match atn.state(state_number)?.transitions().first()?.data() {
7295            Transition::Rule { follow_state, .. } => Some(follow_state),
7296            _ => None,
7297        }
7298    }
7299
7300    #[cfg(test)]
7301    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
7302        caller_follow_token_info_for_stream(&mut self.input, index)
7303    }
7304
7305    /// Runs the fast recognizer once from the rule's start state and returns
7306    /// the best outcome or the per-attempt expected-token accumulator. The
7307    /// caller flips `fast_first_set_prefilter` between calls when a retry is
7308    /// needed, so the FIRST-set cache is left intact across both passes.
7309    fn fast_recognize_top(
7310        &mut self,
7311        atn: &Atn,
7312        request: FastRecognizeTopRequest,
7313        predicate_context: Option<FastPredicateContext<'_>>,
7314    ) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
7315        let FastRecognizeTopRequest {
7316            start_state,
7317            stop_state,
7318            start_index,
7319            precedence,
7320            caller_follow_state,
7321        } = request;
7322        // `input.size()` is intentionally only the currently buffered token
7323        // count here. Do not restore an up-front fill just to size this map:
7324        // a small floor avoids tiny-input churn, and larger inputs reserve from
7325        // the buffered token count without forcing startup tokenization. The
7326        // 8x multiplier matches the empirical
7327        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
7328        // Kotlin ~12× memo entries per token), so the table avoids one
7329        // rehash on the typical hot path.
7330        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
7331        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
7332        recognize_scratch.prepare(memo_capacity);
7333        let mut expected = ExpectedTokens::default();
7334        let empty_recovery = self.empty_recovery_symbols();
7335        let outcomes = self.recognize_state_fast(
7336            atn,
7337            FastRecognizeRequest {
7338                state_number: start_state,
7339                stop_state,
7340                index: start_index,
7341                rule_start_index: start_index,
7342                decision_start_index: None,
7343                precedence,
7344                depth: 0,
7345                recovery_symbols: empty_recovery,
7346                recovery_state: None,
7347            },
7348            FastRecognizeScratch {
7349                predicate_context,
7350                visiting: &mut recognize_scratch.visiting,
7351                memo: &mut recognize_scratch.memo,
7352                expected: &mut expected,
7353                native_depth: 0,
7354            },
7355        );
7356        recognize_scratch.release_oversized_memo();
7357        self.fast_recognize_scratch = recognize_scratch;
7358        #[cfg(feature = "perf-counters")]
7359        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
7360            perf_counters::dump();
7361            perf_counters::reset();
7362        }
7363        let caller_follow =
7364            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
7365        let selected = {
7366            let arena = &self.recognition_arena;
7367            let input = &mut self.input;
7368            select_best_fast_outcome(
7369                outcomes.into_iter(),
7370                self.prediction_mode,
7371                caller_follow.as_deref(),
7372                |index| caller_follow_token_info_for_stream(input, index),
7373                arena,
7374            )
7375        };
7376        match selected {
7377            Some(mut outcome) => {
7378                let alt_number = if self.build_parse_trees || self.fast_track_alt_numbers {
7379                    self.materialize_fast_outcome_nodes(&mut outcome)
7380                } else {
7381                    0
7382                };
7383                Ok((outcome, expected, alt_number))
7384            }
7385            None => Err(expected),
7386        }
7387    }
7388
7389    /// Converts one speculative arena record into the flat public CST.
7390    fn arena_recognized_node_tree(
7391        &mut self,
7392        node_id: RecognizedNodeId,
7393        track_alt_numbers: bool,
7394        track_context_alt_numbers: bool,
7395    ) -> Result<ParseTree, AntlrError> {
7396        let node = self.recognition_arena.node(node_id);
7397        match node {
7398            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
7399            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
7400            ArenaRecognizedNode::MissingToken { extra } => {
7401                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
7402                    RecognitionExtra::MissingToken {
7403                        token_type,
7404                        at_index,
7405                        text,
7406                    } => (*token_type, *at_index as usize, text.clone()),
7407                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
7408                        unreachable!("missing-token node must reference missing-token extra")
7409                    }
7410                };
7411                let (line, column) = self
7412                    .token_at(at_index)
7413                    .map_or((0, 0), |token| (token.line(), token.column()));
7414                let token = self.insert_synthetic_token(token_type, text, line, column)?;
7415                Ok(self.error_tree(token))
7416            }
7417            ArenaRecognizedNode::Rule {
7418                rule_index,
7419                invoking_state,
7420                alt_number,
7421                start_index,
7422                stop_index,
7423                return_values,
7424                children,
7425            } => {
7426                let mut context = ParserRuleContext::with_child_capacity(
7427                    rule_index as usize,
7428                    invoking_state as isize,
7429                    self.recognition_arena.sequence_len(children),
7430                );
7431                if track_alt_numbers {
7432                    context.set_alt_number((alt_number as usize).max(1));
7433                }
7434                if track_context_alt_numbers {
7435                    context.set_context_alt_number(alt_number as usize);
7436                }
7437                if let Some(extra) = return_values {
7438                    let RecognitionExtra::ReturnValues(values) =
7439                        self.recognition_arena.extra(extra)
7440                    else {
7441                        unreachable!("rule node must reference return-values extra");
7442                    };
7443                    for (name, value) in values {
7444                        context.set_int_return(name.clone(), *value);
7445                    }
7446                }
7447                if let Some(token) = self.token_id_at(start_index as usize) {
7448                    self.set_context_start(&mut context, token);
7449                }
7450                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7451                    self.set_context_stop(&mut context, token);
7452                }
7453                let mut cursor = self
7454                    .recognition_arena
7455                    .fold_left_recursive_boundaries(children);
7456                while let Some(link) = self.recognition_arena.link(cursor) {
7457                    let child = self.arena_recognized_node_tree(
7458                        link.head,
7459                        track_alt_numbers,
7460                        track_context_alt_numbers,
7461                    )?;
7462                    self.tree.add_child(&mut context, child);
7463                    cursor = link.tail;
7464                }
7465                Ok(self.rule_node(context))
7466            }
7467            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index, .. } => {
7468                Err(AntlrError::Unsupported(format!(
7469                    "unfolded left-recursive boundary for rule {rule_index}"
7470                )))
7471            }
7472        }
7473    }
7474
7475    fn arena_recognized_node_tree_with_implicit_tokens(
7476        &mut self,
7477        node_id: RecognizedNodeId,
7478        alt_tracking: AltNumberTracking,
7479    ) -> Result<ParseTree, AntlrError> {
7480        let node = self.recognition_arena.node(node_id);
7481        match node {
7482            ArenaRecognizedNode::Rule {
7483                rule_index,
7484                invoking_state,
7485                alt_number,
7486                start_index,
7487                stop_index,
7488                children,
7489                ..
7490            } => {
7491                let mut context = ParserRuleContext::with_child_capacity(
7492                    rule_index as usize,
7493                    invoking_state as isize,
7494                    self.recognition_arena.sequence_len(children),
7495                );
7496                if alt_tracking.public {
7497                    context.set_alt_number((alt_number as usize).max(1));
7498                }
7499                if alt_tracking.context {
7500                    context.set_context_alt_number(alt_number as usize);
7501                }
7502                if let Some(token) = self.token_id_at(start_index as usize) {
7503                    self.set_context_start(&mut context, token);
7504                }
7505                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7506                    self.set_context_stop(&mut context, token);
7507                }
7508                let children = self
7509                    .recognition_arena
7510                    .fold_left_recursive_boundaries(children);
7511                self.add_arena_implicit_token_children(
7512                    &mut context,
7513                    start_index as usize,
7514                    stop_index.map(|index| index as usize),
7515                    children,
7516                    alt_tracking,
7517                )?;
7518                Ok(self.rule_node(context))
7519            }
7520            _ => {
7521                self.arena_recognized_node_tree(node_id, alt_tracking.public, alt_tracking.context)
7522            }
7523        }
7524    }
7525
7526    fn add_arena_implicit_token_children(
7527        &mut self,
7528        context: &mut ParserRuleContext,
7529        start_index: usize,
7530        stop_index: Option<usize>,
7531        mut children: NodeSeqId,
7532        alt_tracking: AltNumberTracking,
7533    ) -> Result<(), AntlrError> {
7534        let mut cursor = Some(start_index);
7535        while let Some(link) = self.recognition_arena.link(children) {
7536            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
7537                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
7538                let child =
7539                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7540                self.tree.add_child(context, child);
7541                if let Some(child_stop) = child_stop {
7542                    let next = self.next_visible_after_token(child_stop);
7543                    cursor = match (cursor, next) {
7544                        (None, _) | (_, None) => None,
7545                        (Some(current), Some(next)) => Some(current.max(next)),
7546                    };
7547                }
7548            } else {
7549                let child =
7550                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7551                self.tree.add_child(context, child);
7552            }
7553            children = link.tail;
7554        }
7555        if let Some(stop) = stop_index {
7556            self.add_visible_terminals_through(context, cursor, stop)?;
7557        }
7558        Ok(())
7559    }
7560
7561    fn add_visible_terminals_before(
7562        &mut self,
7563        context: &mut ParserRuleContext,
7564        cursor: &mut Option<usize>,
7565        before: usize,
7566    ) -> Result<(), AntlrError> {
7567        let Some(stop) = before.checked_sub(1) else {
7568            return Ok(());
7569        };
7570        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
7571        *cursor = next;
7572        Ok(())
7573    }
7574
7575    fn add_visible_terminals_through(
7576        &mut self,
7577        context: &mut ParserRuleContext,
7578        mut cursor: Option<usize>,
7579        stop: usize,
7580    ) -> Result<Option<usize>, AntlrError> {
7581        while let Some(index) = cursor {
7582            if index > stop {
7583                return Ok(Some(index));
7584            }
7585            let token = self
7586                .input
7587                .get_id(index)
7588                .ok_or_else(|| AntlrError::ParserError {
7589                    line: 0,
7590                    column: 0,
7591                    message: format!("missing token at index {index}"),
7592                    offending: None,
7593                })?;
7594            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
7595            let child = self.terminal_tree(token);
7596            self.tree.add_child(context, child);
7597            if is_eof {
7598                return Ok(None);
7599            }
7600            cursor = self.next_visible_after_token(index);
7601        }
7602        Ok(None)
7603    }
7604
7605    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
7606        let next = self.input.next_visible_after(index);
7607        (next != index).then_some(next)
7608    }
7609
7610    /// Parses a generated rule and returns semantic actions reached on the
7611    /// selected ATN path.
7612    ///
7613    /// This slower path preserves action ordering and token intervals for
7614    /// generated code that replays target-specific action templates after the
7615    /// recognizer has chosen one viable parse path.
7616    pub fn parse_atn_rule_with_actions(
7617        &mut self,
7618        atn: &Atn,
7619        rule_index: usize,
7620    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7621        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
7622    }
7623
7624    /// Parses a generated rule and emits ATN actions plus selected rule-init
7625    /// actions reached on the chosen path.
7626    ///
7627    /// Generated parsers use this when a grammar contains rule-level `@init`
7628    /// templates that must run for nested rule invocations. The runtime keeps
7629    /// the action list path-sensitive, so init templates are replayed only for
7630    /// rules that were actually entered by the selected parse.
7631    pub fn parse_atn_rule_with_action_inits(
7632        &mut self,
7633        atn: &Atn,
7634        rule_index: usize,
7635        init_action_rules: &[usize],
7636    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7637        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7638    }
7639
7640    /// Parses a generated rule with optional semantic-action replay features.
7641    ///
7642    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7643    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7644    /// unchanged for grammars that do not request that target template.
7645    pub fn parse_atn_rule_with_action_options(
7646        &mut self,
7647        atn: &Atn,
7648        rule_index: usize,
7649        init_action_rules: &[usize],
7650        track_alt_numbers: bool,
7651    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7652        self.parse_atn_rule_with_runtime_options(
7653            atn,
7654            rule_index,
7655            ParserRuntimeOptions {
7656                init_action_rules,
7657                track_alt_numbers,
7658                ..ParserRuntimeOptions::default()
7659            },
7660        )
7661    }
7662
7663    /// Parses a generated rule with action replay and parser predicate support.
7664    ///
7665    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7666    /// target-template predicate semantics emitted by the generator. Missing
7667    /// entries are treated as true so unsupported predicate-free grammars keep
7668    /// the previous unconditional transition behavior.
7669    pub fn parse_atn_rule_with_runtime_options(
7670        &mut self,
7671        atn: &Atn,
7672        rule_index: usize,
7673        options: ParserRuntimeOptions<'_>,
7674    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7675        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7676    }
7677
7678    /// Parses a generated rule with action replay, parser predicate support,
7679    /// and an initial left-recursive precedence threshold.
7680    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7681        &mut self,
7682        atn: &Atn,
7683        rule_index: usize,
7684        precedence: i32,
7685        options: ParserRuntimeOptions<'_>,
7686    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7687        let ParserRuntimeOptions {
7688            init_action_rules,
7689            track_alt_numbers,
7690            track_context_alt_numbers,
7691            predicates,
7692            semantics,
7693            rule_args,
7694            member_actions,
7695            return_actions,
7696            unknown_predicate_policy,
7697        } = options;
7698        let capture_alt_numbers = track_alt_numbers || track_context_alt_numbers;
7699        if init_action_rules.is_empty()
7700            && !capture_alt_numbers
7701            && predicates.is_empty()
7702            && semantics.is_none()
7703            && rule_args.is_empty()
7704            && member_actions.is_empty()
7705            && return_actions.is_empty()
7706            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7707            && !atn_has_observable_action_transitions(atn)
7708            && !self.semantic_hooks.observes_parser_decisions()
7709            && (!self.semantic_hooks.observes_parser_predicates()
7710                || !atn_has_predicate_transitions(atn))
7711        {
7712            return self
7713                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7714                .map(|tree| (tree, Vec::new()));
7715        }
7716        if !self.semantic_hooks.observes_parser_decisions()
7717            && can_use_fast_predicate_recognizer(atn, &options)
7718        {
7719            self.unknown_predicate_policy = unknown_predicate_policy;
7720            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7721            let member_values = self.int_members.clone();
7722            let result = self
7723                .parse_atn_rule_with_precedence_inner(
7724                    atn,
7725                    rule_index,
7726                    precedence,
7727                    Some(FastPredicateContext {
7728                        predicates,
7729                        semantics,
7730                        member_values: &member_values,
7731                    }),
7732                    AltNumberTracking {
7733                        public: track_alt_numbers,
7734                        context: track_context_alt_numbers,
7735                    },
7736                )
7737                .map(|tree| (tree, Vec::new()));
7738            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7739                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7740            }
7741            return result;
7742        }
7743        self.unknown_predicate_policy = unknown_predicate_policy;
7744        // A generated parent may have already recorded unknown-predicate
7745        // coordinates before descending into this (interpreted) child. Clearing
7746        // unconditionally would drop them before the parent's public entry
7747        // surfaces them, so stash and restore around this call: recognition sees
7748        // only the hits it records itself (so the fail-loud check below reflects
7749        // this rule), and the parent's prior hits are merged back afterward.
7750        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7751        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7752            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7753        })?;
7754        let stop_state = atn
7755            .rule_to_stop_state()
7756            .get(rule_index)
7757            .filter(|state| *state != usize::MAX)
7758            .ok_or_else(|| {
7759                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7760            })?;
7761
7762        let start_index = self.current_visible_index();
7763        self.clear_prediction_diagnostics();
7764        self.reset_per_parse_caches();
7765        self.reset_recognition_arena();
7766        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7767        let invoking_state = self.pending_invoking_states.pop();
7768        let local_int_arg = invoking_state
7769            .and_then(|state| usize::try_from(state).ok())
7770            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7771        let mut visiting = BTreeSet::new();
7772        let mut memo = BTreeMap::new();
7773        let mut expected = ExpectedTokens::default();
7774        let member_values = self.int_members.clone();
7775        let return_values = BTreeMap::new();
7776        let outcomes = self.recognize_state(
7777            atn,
7778            RecognizeRequest {
7779                state_number: start_state,
7780                stop_state,
7781                index: start_index,
7782                rule_start_index: start_index,
7783                decision_start_index: None,
7784                init_action_rules: &init_action_rules,
7785                predicates,
7786                semantics,
7787                rule_args,
7788                member_actions,
7789                return_actions,
7790                local_int_arg,
7791                member_values,
7792                return_values,
7793                rule_alt_number: 0,
7794                track_alt_numbers: capture_alt_numbers,
7795                consumed_eof: false,
7796                committed_decision: false,
7797                precedence,
7798                depth: 0,
7799                recovery_symbols: BTreeSet::new(),
7800                recovery_state: None,
7801            },
7802            &mut visiting,
7803            &mut memo,
7804            &mut expected,
7805        );
7806        if let Some(error) = self.unknown_semantic_error() {
7807            self.report_token_source_errors();
7808            // Keep the recorded coordinates: when this interpreted rule is a
7809            // child of a generated parent, the parent's catch block recovers an
7810            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7811            // coordinate must survive on the parser for the top-level entry's
7812            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7813            // is handled by clearing at the top-level generated entry instead.
7814            return Err(error);
7815        }
7816        // Recognition recorded no unresolved coordinate of its own; merge the
7817        // parent's prior hits back so its public entry can still surface them.
7818        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7819        let Some(outcome) = select_best_outcome(
7820            outcomes.into_iter(),
7821            self.prediction_mode,
7822            &self.recognition_arena,
7823        ) else {
7824            let error = self.recognition_error(rule_index, start_index, &expected);
7825            self.record_syntax_errors(1);
7826            self.report_token_source_errors();
7827            return Err(error);
7828        };
7829
7830        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7831        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7832        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7833        self.report_token_source_errors();
7834        let mut actions = outcome.actions;
7835        if init_action_rules.contains(&rule_index) {
7836            actions.insert(
7837                0,
7838                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7839            );
7840        }
7841        let mut context =
7842            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7843        if track_alt_numbers {
7844            context.set_alt_number(outcome.alt_number.max(1));
7845        }
7846        if track_context_alt_numbers {
7847            context.set_context_alt_number(outcome.alt_number);
7848        }
7849        for (name, value) in outcome.return_values {
7850            context.set_int_return(name, value);
7851        }
7852        if let Some(token) = self.token_id_at(start_index) {
7853            self.set_context_start(&mut context, token);
7854        }
7855        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7856            self.set_context_stop(&mut context, token);
7857        }
7858        let live_root = if self.build_parse_trees {
7859            self.recognition_arena
7860                .fold_left_recursive_boundaries(outcome.nodes)
7861        } else {
7862            outcome.nodes
7863        };
7864        if self.build_parse_trees {
7865            let mut nodes = live_root;
7866            while let Some(link) = self.recognition_arena.link(nodes) {
7867                let child = self.arena_recognized_node_tree(
7868                    link.head,
7869                    track_alt_numbers,
7870                    track_context_alt_numbers,
7871                )?;
7872                self.tree.add_child(&mut context, child);
7873                nodes = link.tail;
7874            }
7875        }
7876        self.finish_recognition_arena(live_root, outcome.diagnostics);
7877        self.input.seek(outcome.index);
7878
7879        let tree = self.rule_node(context);
7880        self.release_tree_scratch_if_idle();
7881        Ok((tree, actions))
7882    }
7883
7884    /// Temporary parser entry used by generated parser methods while the parser
7885    /// ATN simulator is being implemented.
7886    ///
7887    /// This keeps generated parser crates buildable and gives us a stable method
7888    /// surface for every grammar rule. It intentionally accepts all remaining
7889    /// tokens into one rule context; it is not the final parser semantics.
7890    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
7891        let mut context = ParserRuleContext::new(rule_index, self.state());
7892        while self.la(1) != TOKEN_EOF {
7893            let token_type = self.la(1);
7894            let child = self.match_token(token_type)?;
7895            if self.build_parse_trees {
7896                self.tree.add_child(&mut context, child);
7897            }
7898        }
7899        if self.build_parse_trees {
7900            let child = self.match_eof()?;
7901            self.tree.add_child(&mut context, child);
7902        }
7903        let tree = self.rule_node(context);
7904        self.release_tree_scratch_if_idle();
7905        Ok(tree)
7906    }
7907
7908    /// Builds the parser error reported when no ATN path can reach the active
7909    /// rule stop state.
7910    fn recognition_error(
7911        &mut self,
7912        rule_index: usize,
7913        start_index: usize,
7914        expected: &ExpectedTokens,
7915    ) -> AntlrError {
7916        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
7917        self.input.seek(index);
7918        let current = self.input.lt(1);
7919        let line = current.as_ref().map(Token::line).unwrap_or_default();
7920        let column = current.as_ref().map(Token::column).unwrap_or_default();
7921        AntlrError::ParserError {
7922            line,
7923            column,
7924            message,
7925            offending: current.as_ref().map(Token::token_id),
7926        }
7927    }
7928
7929    /// Builds the token index and ANTLR-compatible message for a failed rule.
7930    fn expected_error_message(
7931        &mut self,
7932        rule_index: usize,
7933        start_index: usize,
7934        expected: &ExpectedTokens,
7935    ) -> (usize, String) {
7936        let index = expected
7937            .index
7938            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
7939            .unwrap_or_else(|| self.input.index());
7940        self.input.seek(index);
7941        let current = self.input.lt(1);
7942        let message = if expected
7943            .no_viable
7944            .as_ref()
7945            .is_some_and(|no_viable| no_viable.error_index == index)
7946        {
7947            let start = expected
7948                .no_viable
7949                .as_ref()
7950                .map_or(start_index, |no_viable| no_viable.start_index);
7951            let text = display_input_text(&self.input.text(start, index));
7952            format!("no viable alternative at input '{text}'")
7953        } else if expected.symbols.is_empty() {
7954            if expected.index.is_some() {
7955                let found = current
7956                    .as_ref()
7957                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
7958                if current
7959                    .as_ref()
7960                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
7961                {
7962                    format!(
7963                        "missing {} at {found}",
7964                        self.expected_symbols_display(&expected.symbols)
7965                    )
7966                } else {
7967                    format!("mismatched input {found}")
7968                }
7969            } else {
7970                format!("no viable alternative while parsing rule {rule_index}")
7971            }
7972        } else {
7973            format!(
7974                "mismatched input {} expecting {}",
7975                current
7976                    .as_ref()
7977                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7978                self.expected_symbols_display(&expected.symbols)
7979            )
7980        };
7981        (index, message)
7982    }
7983
7984    /// Converts a failed child rule into a recovered outcome so the parent can
7985    /// continue after reporting the child diagnostic.
7986    fn child_rule_failure_recovery(
7987        &mut self,
7988        rule_index: usize,
7989        start_index: usize,
7990        sync_symbols: &BTreeSet<i32>,
7991        member_values: BTreeMap<usize, i64>,
7992        expected: &ExpectedTokens,
7993    ) -> Option<RecognizeOutcome> {
7994        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
7995        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
7996        let mut next_index = error_index;
7997        loop {
7998            let symbol = self.token_type_at(next_index);
7999            if sync_symbols.contains(&symbol) {
8000                if next_index == error_index {
8001                    return None;
8002                }
8003                break;
8004            }
8005            if symbol == TOKEN_EOF {
8006                break;
8007            }
8008            let after = self.consume_index(next_index, symbol);
8009            if after == next_index {
8010                break;
8011            }
8012            next_index = after;
8013        }
8014        let mut nodes = NodeSeqId::EMPTY;
8015        let error = self.arena_token_node(error_index, true);
8016        self.arena_prepend(&mut nodes, error);
8017        let diagnostics = self
8018            .recognition_arena
8019            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8020        Some(RecognizeOutcome {
8021            index: next_index,
8022            consumed_eof: false,
8023            alt_number: 0,
8024            member_values,
8025            return_values: BTreeMap::new(),
8026            diagnostics,
8027            decisions: Vec::new(),
8028            actions: Vec::new(),
8029            nodes,
8030        })
8031    }
8032
8033    /// Adapts the optional recovery result to the normal outcome list used by
8034    /// rule-call transitions.
8035    fn child_rule_failure_recovery_outcomes(
8036        &mut self,
8037        request: ChildRuleFailureRecovery<'_>,
8038    ) -> Vec<RecognizeOutcome> {
8039        let sync_symbols =
8040            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
8041        self.child_rule_failure_recovery(
8042            request.rule_index,
8043            request.start_index,
8044            &sync_symbols,
8045            request.member_values,
8046            request.expected,
8047        )
8048        .into_iter()
8049        .collect()
8050    }
8051
8052    /// Formats expected token types using ANTLR's single-token or set syntax.
8053    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
8054        expected_symbols_display(symbols, self.vocabulary())
8055    }
8056
8057    /// Returns the single-token deletion repair if the token after `index`
8058    /// satisfies the failed consuming transition.
8059    fn single_token_deletion(
8060        &mut self,
8061        transition: ParserTransition<'_>,
8062        index: usize,
8063        max_token_type: i32,
8064        expected_symbols: &BTreeSet<i32>,
8065    ) -> Option<(ParserDiagnostic, usize, i32)> {
8066        let current_symbol = self.token_type_at(index);
8067        if current_symbol == TOKEN_EOF {
8068            return None;
8069        }
8070        let next_index = self.consume_index(index, current_symbol);
8071        if next_index == index {
8072            return None;
8073        }
8074        let next_symbol = self.token_type_at(next_index);
8075        if !transition.matches(next_symbol, 1, max_token_type) {
8076            return None;
8077        }
8078        let transition_expected = transition_expected_symbols(transition, max_token_type);
8079        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8080            &transition_expected
8081        } else {
8082            expected_symbols
8083        });
8084        let current = self.token_at(index);
8085        let message = format!(
8086            "extraneous input {} expecting {expected_display}",
8087            current
8088                .as_ref()
8089                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8090        );
8091        Some((
8092            diagnostic_for_token(current, message),
8093            next_index,
8094            next_symbol,
8095        ))
8096    }
8097
8098    /// Returns the repair used when deleting the current token lets a recovery
8099    /// state continue with the following token.
8100    fn current_token_deletion(
8101        &mut self,
8102        index: usize,
8103        expected_symbols: &BTreeSet<i32>,
8104    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
8105        if expected_symbols.is_empty() {
8106            return None;
8107        }
8108        let current_symbol = self.token_type_at(index);
8109        if current_symbol == TOKEN_EOF {
8110            return None;
8111        }
8112        let current = self.token_at(index);
8113        let message = format!(
8114            "extraneous input {} expecting {}",
8115            current
8116                .as_ref()
8117                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8118            self.expected_symbols_display(expected_symbols)
8119        );
8120        let diagnostic = diagnostic_for_token(current, message);
8121        let mut skipped = Vec::new();
8122        let mut cursor = index;
8123        loop {
8124            let symbol = self.token_type_at(cursor);
8125            if symbol == TOKEN_EOF {
8126                return None;
8127            }
8128            skipped.push(cursor);
8129            let next_index = self.consume_index(cursor, symbol);
8130            if next_index == cursor {
8131                return None;
8132            }
8133            let next_symbol = self.token_type_at(next_index);
8134            if expected_symbols.contains(&next_symbol) {
8135                return Some((diagnostic, next_index, skipped));
8136            }
8137            cursor = next_index;
8138        }
8139    }
8140
8141    /// Returns the single-token insertion repair for a failed consuming
8142    /// transition. The caller validates the repair by continuing from the
8143    /// transition target at the same input index.
8144    fn single_token_insertion(
8145        &mut self,
8146        transition: ParserTransition<'_>,
8147        index: usize,
8148        max_token_type: i32,
8149        expected_symbols: &BTreeSet<i32>,
8150        follow_symbols: &BTreeSet<i32>,
8151    ) -> Option<(ParserDiagnostic, i32, String)> {
8152        let current_symbol = self.token_type_at(index);
8153        if !follow_symbols.contains(&current_symbol) {
8154            return None;
8155        }
8156        let transition_expected = transition_expected_symbols(transition, max_token_type);
8157        let token_type = transition_expected.iter().next().copied()?;
8158        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8159            &transition_expected
8160        } else {
8161            expected_symbols
8162        });
8163        let mut token_symbols = BTreeSet::new();
8164        token_symbols.insert(token_type);
8165        let missing_token_display = self.expected_symbols_display(&token_symbols);
8166        let current = self.token_at(index);
8167        let message = format!(
8168            "missing {expected_display} at {}",
8169            current
8170                .as_ref()
8171                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8172        );
8173        let text = format!("<missing {missing_token_display}>");
8174        Some((
8175            diagnostic_for_token(current.as_ref(), message),
8176            token_type,
8177            text,
8178        ))
8179    }
8180
8181    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
8182    /// skip the unexpected current token when the following token satisfies the
8183    /// transition that failed.
8184    fn fast_single_token_deletion_recovery(
8185        &mut self,
8186        recovery: FastRecoveryRequest<'_, '_>,
8187        predicate_context: Option<FastPredicateContext<'_>>,
8188    ) -> Vec<FastRecognizeOutcome> {
8189        let FastRecoveryRequest {
8190            atn,
8191            transition,
8192            expected_symbols,
8193            target,
8194            request,
8195            visiting,
8196            memo,
8197            expected,
8198        } = recovery;
8199        let FastRecognizeRequest {
8200            stop_state,
8201            index,
8202            rule_start_index,
8203            decision_start_index,
8204            precedence,
8205            depth,
8206            ..
8207        } = request;
8208        let Some((diagnostic, next_index, next_symbol)) =
8209            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8210        else {
8211            return Vec::new();
8212        };
8213        let after_next = self.consume_index(next_index, next_symbol);
8214        let empty_recovery = self.empty_recovery_symbols();
8215        self.recognize_state_fast(
8216            atn,
8217            FastRecognizeRequest {
8218                state_number: target,
8219                stop_state,
8220                index: after_next,
8221                rule_start_index,
8222                decision_start_index,
8223                precedence,
8224                depth: depth + 1,
8225                recovery_symbols: empty_recovery,
8226                recovery_state: None,
8227            },
8228            FastRecognizeScratch {
8229                predicate_context,
8230                visiting,
8231                memo,
8232                expected,
8233                native_depth: 0,
8234            },
8235        )
8236        .into_iter()
8237        .map(|mut outcome| {
8238            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
8239            outcome.diagnostics = self
8240                .recognition_arena
8241                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8242            if self.fast_token_nodes_enabled {
8243                let token = self.arena_token_node(next_index, false);
8244                self.defer_fast_outcome_node(&mut outcome, token);
8245                let error = self.arena_token_node(index, true);
8246                self.defer_fast_outcome_node(&mut outcome, error);
8247            }
8248            outcome
8249        })
8250        .collect()
8251    }
8252
8253    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
8254    /// pretend the expected transition token was present and continue without
8255    /// consuming the current token.
8256    fn fast_single_token_insertion_recovery(
8257        &mut self,
8258        recovery: FastRecoveryRequest<'_, '_>,
8259        predicate_context: Option<FastPredicateContext<'_>>,
8260    ) -> Vec<FastRecognizeOutcome> {
8261        let FastRecoveryRequest {
8262            atn,
8263            transition,
8264            expected_symbols,
8265            target,
8266            request,
8267            visiting,
8268            memo,
8269            expected,
8270        } = recovery;
8271        let FastRecognizeRequest {
8272            stop_state,
8273            index,
8274            rule_start_index,
8275            decision_start_index,
8276            precedence,
8277            depth,
8278            ..
8279        } = request;
8280        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
8281        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
8282            transition,
8283            index,
8284            atn.max_token_type(),
8285            &expected_symbols,
8286            &follow_symbols,
8287        ) else {
8288            return Vec::new();
8289        };
8290        let empty_recovery = self.empty_recovery_symbols();
8291        self.recognize_state_fast(
8292            atn,
8293            FastRecognizeRequest {
8294                state_number: target,
8295                stop_state,
8296                index,
8297                rule_start_index,
8298                decision_start_index,
8299                precedence,
8300                depth: depth + 1,
8301                recovery_symbols: empty_recovery,
8302                recovery_state: None,
8303            },
8304            FastRecognizeScratch {
8305                predicate_context,
8306                visiting,
8307                memo,
8308                expected,
8309                native_depth: 0,
8310            },
8311        )
8312        .into_iter()
8313        .map(|mut outcome| {
8314            outcome.diagnostics = self
8315                .recognition_arena
8316                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8317            let missing = self.arena_missing_token_node(token_type, index, text.clone());
8318            self.defer_fast_outcome_node(&mut outcome, missing);
8319            outcome
8320        })
8321        .collect()
8322    }
8323
8324    /// Retries the current fast-recognition state after deleting one
8325    /// unexpected token that precedes a valid loop or block continuation.
8326    fn fast_current_token_deletion_recovery(
8327        &mut self,
8328        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
8329        predicate_context: Option<FastPredicateContext<'_>>,
8330    ) -> Vec<FastRecognizeOutcome> {
8331        let FastCurrentTokenDeletionRequest {
8332            atn,
8333            expected_symbols,
8334            mut request,
8335            visiting,
8336            memo,
8337            expected,
8338        } = recovery;
8339        if request.index == request.rule_start_index {
8340            return Vec::new();
8341        }
8342        let Some((diagnostic, next_index, skipped)) =
8343            self.current_token_deletion(request.index, &expected_symbols)
8344        else {
8345            return Vec::new();
8346        };
8347        request.state_number = request.recovery_state.unwrap_or(request.state_number);
8348        request.index = next_index;
8349        request.depth += 1;
8350        request.recovery_state = None;
8351        self.recognize_state_fast(
8352            atn,
8353            request,
8354            FastRecognizeScratch {
8355                predicate_context,
8356                visiting,
8357                memo,
8358                expected,
8359                native_depth: 0,
8360            },
8361        )
8362        .into_iter()
8363        .map(|mut outcome| {
8364            outcome.diagnostics = self
8365                .recognition_arena
8366                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8367            for index in skipped.iter().rev() {
8368                let error = self.arena_token_node(*index, true);
8369                self.defer_fast_outcome_node(&mut outcome, error);
8370            }
8371            outcome
8372        })
8373        .collect()
8374    }
8375
8376    /// Converts a failed child rule into a recovered fast-recognizer outcome so
8377    /// the parent can keep its child rule context and continue at a sync token.
8378    fn fast_child_rule_failure_recovery(
8379        &mut self,
8380        rule_index: usize,
8381        start_index: usize,
8382        sync_symbols: &BTreeSet<i32>,
8383        expected: &ExpectedTokens,
8384    ) -> Option<FastRecognizeOutcome> {
8385        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
8386        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
8387        let mut next_index = error_index;
8388        loop {
8389            let symbol = self.token_type_at(next_index);
8390            if sync_symbols.contains(&symbol) {
8391                if next_index == error_index {
8392                    return None;
8393                }
8394                break;
8395            }
8396            if symbol == TOKEN_EOF {
8397                break;
8398            }
8399            let after = self.consume_index(next_index, symbol);
8400            if after == next_index {
8401                break;
8402            }
8403            next_index = after;
8404        }
8405        let diagnostics = self
8406            .recognition_arena
8407            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8408        let mut nodes = NodeSeqId::EMPTY;
8409        if self.fast_token_nodes_enabled {
8410            let error = self.arena_token_node(error_index, true);
8411            self.arena_prepend(&mut nodes, error);
8412        }
8413        Some(FastRecognizeOutcome {
8414            index: next_index,
8415            consumed_eof: false,
8416            diagnostics,
8417            deferred_nodes: FastDeferredNodeId::EMPTY,
8418            nodes,
8419        })
8420    }
8421
8422    /// Adapts the optional child-rule recovery result to the fast-recognizer
8423    /// outcome list used by rule-call transitions.
8424    fn fast_child_rule_failure_recovery_outcomes(
8425        &mut self,
8426        request: FastChildRuleFailureRecoveryRequest<'_>,
8427    ) -> Vec<FastRecognizeOutcome> {
8428        let FastChildRuleFailureRecoveryRequest {
8429            atn,
8430            rule_index,
8431            start_index,
8432            follow_state,
8433            stop_state,
8434            expected,
8435        } = request;
8436        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
8437        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
8438            .into_iter()
8439            .collect()
8440    }
8441
8442    fn defer_fast_outcome_node(
8443        &mut self,
8444        outcome: &mut FastRecognizeOutcome,
8445        node: RecognizedNodeId,
8446    ) {
8447        if outcome.deferred_nodes.is_empty() {
8448            self.arena_prepend(&mut outcome.nodes, node);
8449            return;
8450        }
8451        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
8452        let fragment = self.recognition_arena.deferred_fragment(fragment);
8453        outcome.deferred_nodes = self
8454            .recognition_arena
8455            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
8456    }
8457
8458    fn defer_fast_outcome_alternative(
8459        &mut self,
8460        outcome: &mut FastRecognizeOutcome,
8461        alt_number: usize,
8462    ) {
8463        let alternative = self.recognition_arena.deferred_alternative(alt_number);
8464        outcome.deferred_nodes = self
8465            .recognition_arena
8466            .concat_deferred_nodes(alternative, outcome.deferred_nodes);
8467    }
8468
8469    fn defer_fast_outcome_boundary(
8470        &mut self,
8471        outcome: &mut FastRecognizeOutcome,
8472        rule_index: usize,
8473    ) {
8474        let boundary = self
8475            .recognition_arena
8476            .deferred_left_recursive_boundary(rule_index);
8477        outcome.deferred_nodes = self
8478            .recognition_arena
8479            .concat_deferred_nodes(boundary, outcome.deferred_nodes);
8480    }
8481
8482    fn materialize_fast_deferred_nodes(
8483        &mut self,
8484        root: FastDeferredNodeId,
8485        initial_suffix: NodeSeqId,
8486    ) -> (NodeSeqId, usize) {
8487        if root.is_empty() {
8488            return (initial_suffix, 0);
8489        }
8490
8491        enum Frame {
8492            Visit(FastDeferredNodeId),
8493            ContinuePrefix(FastDeferredNodeId),
8494            FinishRule {
8495                rule: FastDeferredRule,
8496                parent_suffix: NodeSeqId,
8497                parent_alt_number: u32,
8498                parent_pending_boundary: Option<RecognizedNodeId>,
8499            },
8500        }
8501
8502        let mut result = initial_suffix;
8503        // The rope is visited suffix-first while nodes are prepended. Later
8504        // alternatives arrive first, so earlier markers overwrite them; a
8505        // boundary redirects those earlier markers to the wrapped context.
8506        let mut alt_number = 0;
8507        let mut pending_boundary = None;
8508        let mut pending = Vec::with_capacity(16);
8509        pending.push(Frame::Visit(root));
8510        let mut fragment_nodes = Vec::new();
8511        while let Some(frame) = pending.pop() {
8512            match frame {
8513                Frame::Visit(deferred) => {
8514                    if deferred.is_empty() {
8515                        continue;
8516                    }
8517
8518                    match self.recognition_arena.deferred_node(deferred) {
8519                        FastDeferredNode::Fragment(sequence) => {
8520                            fragment_nodes.clear();
8521                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
8522                            while let Some(node) = fragment_nodes.pop() {
8523                                self.arena_prepend(&mut result, node);
8524                            }
8525                        }
8526                        FastDeferredNode::Rule(rule) => {
8527                            let rule = self.recognition_arena.deferred_rule(rule);
8528                            let parent_suffix = result;
8529                            let parent_alt_number = alt_number;
8530                            let parent_pending_boundary = pending_boundary;
8531                            result = rule.children;
8532                            alt_number = 0;
8533                            pending_boundary = None;
8534                            pending.push(Frame::FinishRule {
8535                                rule,
8536                                parent_suffix,
8537                                parent_alt_number,
8538                                parent_pending_boundary,
8539                            });
8540                            pending.push(Frame::Visit(rule.deferred_children));
8541                        }
8542                        FastDeferredNode::Alternative(selected) => {
8543                            if let Some(boundary) = pending_boundary {
8544                                self.recognition_arena
8545                                    .set_boundary_alt_number(boundary, selected);
8546                            } else {
8547                                alt_number = selected;
8548                            }
8549                        }
8550                        FastDeferredNode::LeftRecursiveBoundary { rule_index } => {
8551                            let boundary = self.arena_boundary_node(rule_index as usize, 0);
8552                            self.arena_prepend(&mut result, boundary);
8553                            pending_boundary = Some(boundary);
8554                        }
8555                        FastDeferredNode::Concat {
8556                            prefix,
8557                            suffix: deferred_suffix,
8558                        } => {
8559                            pending.push(Frame::ContinuePrefix(prefix));
8560                            pending.push(Frame::Visit(deferred_suffix));
8561                        }
8562                    }
8563                }
8564                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
8565                Frame::FinishRule {
8566                    rule,
8567                    parent_suffix,
8568                    parent_alt_number,
8569                    parent_pending_boundary,
8570                } => {
8571                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
8572                        rule_index: rule.rule_index,
8573                        invoking_state: rule.invoking_state,
8574                        alt_number,
8575                        start_index: rule.start_index,
8576                        stop_index: rule.stop_index,
8577                        return_values: None,
8578                        children: result,
8579                    });
8580                    result = parent_suffix;
8581                    self.arena_prepend(&mut result, node);
8582                    alt_number = parent_alt_number;
8583                    pending_boundary = parent_pending_boundary;
8584                }
8585            }
8586        }
8587        (result, alt_number as usize)
8588    }
8589
8590    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) -> usize {
8591        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
8592        let (nodes, alt_number) =
8593            self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
8594        outcome.nodes = nodes;
8595        alt_number
8596    }
8597
8598    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
8599    /// heap work instead of the native call stack.
8600    fn recognize_repetition_fast(
8601        &mut self,
8602        atn: &Atn,
8603        request: &FastRecognizeRequest,
8604        shape: FastRepetitionShape,
8605        scratch: FastRecognizeScratch<'_, '_>,
8606    ) -> Vec<FastRecognizeOutcome> {
8607        let FastRecognizeScratch {
8608            predicate_context,
8609            visiting,
8610            memo,
8611            expected,
8612            native_depth,
8613        } = scratch;
8614        let lookahead = if self.fast_first_set_prefilter {
8615            atn.state(request.state_number).and_then(|state| {
8616                state
8617                    .rule_index()
8618                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8619                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
8620            })
8621        } else {
8622            None
8623        };
8624        let (enter_alt_number, exit_alt_number) = if self.fast_track_alt_numbers {
8625            let state = atn
8626                .state(request.state_number)
8627                .expect("repetition request state must exist");
8628            (
8629                next_alt_number(state, 2, shape.enter_transition_index, 0, true),
8630                next_alt_number(state, 2, shape.exit_transition_index, 0, true),
8631            )
8632        } else {
8633            (0, 0)
8634        };
8635        let mut work = Vec::with_capacity(2);
8636        push_fast_repetition_work(
8637            &mut work,
8638            shape,
8639            FastRepetitionPath {
8640                index: request.index,
8641                deferred_nodes: FastDeferredNodeId::EMPTY,
8642                diagnostics: DiagnosticSeqId::EMPTY,
8643                consumed_eof: false,
8644            },
8645            lookahead.as_deref(),
8646            self.token_type_at(request.index),
8647        );
8648        let mut coordinates = FastRepetitionCoordinates::new(request.index);
8649        let mut outcomes = Vec::new();
8650        while let Some(item) = work.pop() {
8651            match item {
8652                FastRepetitionWork::Enter(path) => {
8653                    if !coordinates.insert_entered(path) {
8654                        continue;
8655                    }
8656                    let path_nodes = if enter_alt_number == 0 {
8657                        path.deferred_nodes
8658                    } else {
8659                        let alternative = self
8660                            .recognition_arena
8661                            .deferred_alternative(enter_alt_number);
8662                        self.recognition_arena
8663                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8664                    };
8665                    let body_outcomes = self.recognize_state_fast(
8666                        atn,
8667                        FastRecognizeRequest {
8668                            state_number: shape.enter_target,
8669                            stop_state: shape.body_stop_state,
8670                            index: path.index,
8671                            rule_start_index: request.rule_start_index,
8672                            decision_start_index: request.decision_start_index,
8673                            precedence: request.precedence,
8674                            depth: request.depth.saturating_add(1),
8675                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8676                            recovery_state: request.recovery_state,
8677                        },
8678                        FastRecognizeScratch {
8679                            predicate_context,
8680                            visiting: &mut *visiting,
8681                            memo: &mut *memo,
8682                            expected: &mut *expected,
8683                            native_depth: native_depth + 1,
8684                        },
8685                    );
8686                    for body in body_outcomes.into_iter().rev() {
8687                        // ANTLR rejects nullable repetition bodies. Keep the
8688                        // interpreter bounded for malformed or recovered ATNs
8689                        // by mirroring the existing same-coordinate cycle cut.
8690                        if body.index <= path.index {
8691                            continue;
8692                        }
8693                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
8694                        let body_nodes = self
8695                            .recognition_arena
8696                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
8697                        let deferred_nodes = self
8698                            .recognition_arena
8699                            .concat_deferred_nodes(path_nodes, body_nodes);
8700                        let next_path = FastRepetitionPath {
8701                            index: body.index,
8702                            deferred_nodes,
8703                            diagnostics: self
8704                                .recognition_arena
8705                                .concat_diagnostics(path.diagnostics, body.diagnostics),
8706                            consumed_eof: path.consumed_eof || body.consumed_eof,
8707                        };
8708                        let symbol = self.token_type_at(next_path.index);
8709                        push_fast_repetition_work(
8710                            &mut work,
8711                            shape,
8712                            next_path,
8713                            lookahead.as_deref(),
8714                            symbol,
8715                        );
8716                    }
8717                }
8718                FastRepetitionWork::Exit(path) => {
8719                    if !coordinates.insert_exited(path) {
8720                        continue;
8721                    }
8722                    let path_nodes = if exit_alt_number == 0 {
8723                        path.deferred_nodes
8724                    } else {
8725                        let alternative =
8726                            self.recognition_arena.deferred_alternative(exit_alt_number);
8727                        self.recognition_arena
8728                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8729                    };
8730                    let suffixes = self.recognize_state_fast(
8731                        atn,
8732                        FastRecognizeRequest {
8733                            state_number: shape.exit_target,
8734                            stop_state: request.stop_state,
8735                            index: path.index,
8736                            rule_start_index: request.rule_start_index,
8737                            decision_start_index: request.decision_start_index,
8738                            precedence: request.precedence,
8739                            depth: request.depth.saturating_add(1),
8740                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8741                            recovery_state: request.recovery_state,
8742                        },
8743                        FastRecognizeScratch {
8744                            predicate_context,
8745                            visiting: &mut *visiting,
8746                            memo: &mut *memo,
8747                            expected: &mut *expected,
8748                            native_depth: native_depth + 1,
8749                        },
8750                    );
8751                    for mut outcome in suffixes {
8752                        outcome.deferred_nodes = self
8753                            .recognition_arena
8754                            .concat_deferred_nodes(path_nodes, outcome.deferred_nodes);
8755                        outcome.diagnostics = self
8756                            .recognition_arena
8757                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8758                        outcome.consumed_eof |= path.consumed_eof;
8759                        outcomes.push(outcome);
8760                    }
8761                }
8762            }
8763        }
8764        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8765        outcomes
8766    }
8767
8768    /// Attempts to reach `stop_state` from `state_number` without committing
8769    /// token consumption to the parser's public stream position.
8770    fn recognize_state_fast(
8771        &mut self,
8772        atn: &Atn,
8773        request: FastRecognizeRequest,
8774        scratch: FastRecognizeScratch<'_, '_>,
8775    ) -> Vec<FastRecognizeOutcome> {
8776        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
8777            return self.recognize_state_fast_inner(atn, request, scratch);
8778        }
8779        self.recognize_state_fast_checked(atn, request, scratch)
8780    }
8781
8782    #[inline(never)]
8783    fn recognize_state_fast_checked(
8784        &mut self,
8785        atn: &Atn,
8786        request: FastRecognizeRequest,
8787        mut scratch: FastRecognizeScratch<'_, '_>,
8788    ) -> Vec<FastRecognizeOutcome> {
8789        scratch.native_depth = 1;
8790        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
8791            self.recognize_state_fast_inner(atn, request, scratch)
8792        })
8793    }
8794
8795    #[allow(clippy::too_many_lines)]
8796    fn recognize_state_fast_inner(
8797        &mut self,
8798        atn: &Atn,
8799        request: FastRecognizeRequest,
8800        scratch: FastRecognizeScratch<'_, '_>,
8801    ) -> Vec<FastRecognizeOutcome> {
8802        #[cfg(feature = "perf-counters")]
8803        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8804        let FastRecognizeScratch {
8805            predicate_context,
8806            visiting,
8807            memo,
8808            expected,
8809            native_depth,
8810        } = scratch;
8811        let FastRecognizeRequest {
8812            mut state_number,
8813            stop_state,
8814            mut index,
8815            rule_start_index,
8816            decision_start_index,
8817            precedence,
8818            mut depth,
8819            recovery_symbols,
8820            recovery_state,
8821        } = request;
8822        let max_token_type = atn.max_token_type();
8823        // Walk straight-line epsilon chains in a loop instead of recursing
8824        // into `recognize_state_fast` for each intermediate state. ATN
8825        // serialization places long sequences of `BasicBlock` epsilon
8826        // transitions between decisions: turning that chain into a loop
8827        // collapses many recursive calls (and their memo lookups, vec
8828        // allocations, and visit-set churn) into a single function frame.
8829        // The loop exits as soon as we hit the original state's logic
8830        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8831        // precedence) so existing fanout, recovery, and memoization still
8832        // apply unchanged.
8833        //
8834        // The inline case also handles single-atom-match states on the
8835        // happy-pass path: when the lone consuming transition matches the
8836        // current lookahead, advance the index and continue without paying
8837        // for a full `recognize_state_fast` recursion. We track tokens we
8838        // consumed inline in `inline_consumed_tokens` so they can be
8839        // prepended onto the eventual outcome list once we hit a state
8840        // whose handling falls outside this fast loop.
8841        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8842        let mut inline_consumed_eof = false;
8843        loop {
8844            if depth > RECOGNITION_DEPTH_LIMIT {
8845                return Vec::new();
8846            }
8847            if state_number == stop_state {
8848                let mut nodes = NodeSeqId::EMPTY;
8849                if self.fast_token_nodes_enabled {
8850                    for token_index in inline_consumed_tokens.iter().rev() {
8851                        let token = self.arena_token_node(*token_index, false);
8852                        self.arena_prepend(&mut nodes, token);
8853                    }
8854                }
8855                return vec![FastRecognizeOutcome {
8856                    index,
8857                    consumed_eof: inline_consumed_eof,
8858                    diagnostics: DiagnosticSeqId::EMPTY,
8859                    deferred_nodes: FastDeferredNodeId::EMPTY,
8860                    nodes,
8861                }];
8862            }
8863            let Some(state) = atn.state(state_number) else {
8864                return Vec::new();
8865            };
8866            let transitions = state.transitions();
8867            if transitions.len() == 1 && !state.precedence_rule_decision() {
8868                let transition = transitions
8869                    .first()
8870                    .expect("single transition checked above");
8871                let transition_kind = transition.kind();
8872                let target = transition.target();
8873                match transition_kind {
8874                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
8875                        if left_recursive_boundary(atn, state, target).is_none() =>
8876                    {
8877                        #[cfg(feature = "perf-counters")]
8878                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8879                        state_number = target;
8880                        depth += 1;
8881                        continue;
8882                    }
8883                    ParserTransitionKind::Predicate
8884                        if left_recursive_boundary(atn, state, target).is_none() =>
8885                    {
8886                        #[cfg(feature = "perf-counters")]
8887                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8888                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
8889                        {
8890                            record_predicate_no_viable(expected, decision_start_index, index);
8891                            return Vec::new();
8892                        }
8893                        state_number = target;
8894                        depth += 1;
8895                        continue;
8896                    }
8897                    ParserTransitionKind::Precedence
8898                        if packed_i32(transition.arg0()) >= precedence
8899                            && left_recursive_boundary(atn, state, target).is_none() =>
8900                    {
8901                        #[cfg(feature = "perf-counters")]
8902                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
8903                        state_number = target;
8904                        depth += 1;
8905                        continue;
8906                    }
8907                    // Single-atom / range / set / wildcard / not-set states
8908                    // are common (~17K of ~125K calls on C#) and almost
8909                    // always succeed in pass 1: no fanout, no recovery, no
8910                    // diagnostics. Inline the token match and continue
8911                    // walking instead of recursing — the recursive path
8912                    // would just allocate a Vec, build one outcome, prepend
8913                    // a Token node, and return. Skip pass 2 (recovery
8914                    // enabled): there the failure branch matters and the
8915                    // existing recursive code records expected symbols.
8916                    ParserTransitionKind::Atom
8917                    | ParserTransitionKind::Range
8918                    | ParserTransitionKind::Set
8919                    | ParserTransitionKind::NotSet
8920                    | ParserTransitionKind::Wildcard
8921                        if !self.fast_recovery_enabled =>
8922                    {
8923                        let symbol = self.token_type_at(index);
8924                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
8925                            #[cfg(feature = "perf-counters")]
8926                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
8927                            if self.fast_token_nodes_enabled {
8928                                inline_consumed_tokens.push(index);
8929                            }
8930                            inline_consumed_eof |= symbol == TOKEN_EOF;
8931                            index = self.consume_index(index, symbol);
8932                            state_number = target;
8933                            depth += 1;
8934                            continue;
8935                        }
8936                        // Fall through to break and let the regular
8937                        // body handle the no-match case (returns empty).
8938                    }
8939                    _ => {}
8940                }
8941            }
8942            break;
8943        }
8944        // If we collected token nodes inline but bail to the recursive
8945        // body (decision state, rule call, etc.), the outcomes returned
8946        // below will need those token nodes prepended.
8947        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
8948        let Some(state) = atn.state(state_number) else {
8949            return Vec::new();
8950        };
8951        let transitions = state.transitions();
8952        let transition_count = transitions.len();
8953        if !self.fast_recovery_enabled
8954            && let Some(shape) = fast_repetition_shape(atn, state)
8955        {
8956            let mut outcomes = self.recognize_repetition_fast(
8957                atn,
8958                &FastRecognizeRequest {
8959                    state_number,
8960                    stop_state,
8961                    index,
8962                    rule_start_index,
8963                    decision_start_index,
8964                    precedence,
8965                    depth,
8966                    recovery_symbols: Rc::clone(&recovery_symbols),
8967                    recovery_state,
8968                },
8969                shape,
8970                FastRecognizeScratch {
8971                    predicate_context,
8972                    visiting: &mut *visiting,
8973                    memo: &mut *memo,
8974                    expected: &mut *expected,
8975                    native_depth: native_depth + 1,
8976                },
8977            );
8978            if inline_pending {
8979                for outcome in &mut outcomes {
8980                    outcome.consumed_eof |= inline_consumed_eof;
8981                    if self.fast_token_nodes_enabled {
8982                        for token_index in inline_consumed_tokens.iter().rev() {
8983                            let token = self.arena_token_node(*token_index, false);
8984                            self.defer_fast_outcome_node(outcome, token);
8985                        }
8986                    }
8987                }
8988            }
8989            return outcomes;
8990        }
8991        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
8992        // fields and the rule/decision boundary indices are pure plumbing —
8993        // they only affect the recovery branch and the no-viable diagnostic
8994        // recording, neither of which fires when recovery is off. Zeroing
8995        // them in the memo key collapses calls that visit the same
8996        // `(state, index)` from different rule-call sites onto one cache
8997        // entry, which is the dominant cost on large grammars (e.g. C#) where
8998        // many rules eventually delegate into the same `expression` /
8999        // `primary_expression` / `type` branches.
9000        let key = if self.fast_recovery_enabled {
9001            FastRecognizeKey {
9002                state_number,
9003                stop_state,
9004                index,
9005                rule_start_index,
9006                decision_start_index,
9007                precedence,
9008                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
9009                recovery_state,
9010            }
9011        } else {
9012            FastRecognizeKey {
9013                state_number,
9014                stop_state,
9015                index,
9016                rule_start_index: 0,
9017                decision_start_index: None,
9018                precedence,
9019                recovery_symbols_id: 0,
9020                recovery_state: None,
9021            }
9022        };
9023        // Once the clean-pass probe has established that coordinates do not
9024        // repeat, stop paying for the full memo table. Recovery always keeps
9025        // memoization because cached failures carry diagnostics, while
9026        // repeat-heavy clean parses promote before reaching sparse mode.
9027        let memo_lookup_enabled = self.fast_recovery_enabled
9028            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
9029        if memo_lookup_enabled {
9030            if let Some(outcomes) = memo.get(&key) {
9031                #[cfg(feature = "perf-counters")]
9032                {
9033                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
9034                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
9035                }
9036                // Materialize a fresh `Vec` from the cached slice; the caller
9037                // mutates per-outcome state (eof flags, prepended nodes) so we
9038                // can't hand them the shared backing.
9039                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
9040                    let inline_eof = inline_consumed_eof;
9041                    let inline_tokens = &inline_consumed_tokens;
9042                    return outcomes
9043                        .iter()
9044                        .copied()
9045                        .map(|mut outcome| {
9046                            if inline_eof {
9047                                outcome.consumed_eof = true;
9048                            }
9049                            if self.fast_token_nodes_enabled {
9050                                for token_index in inline_tokens.iter().rev() {
9051                                    let token = self.arena_token_node(*token_index, false);
9052                                    self.defer_fast_outcome_node(&mut outcome, token);
9053                                }
9054                            }
9055                            outcome
9056                        })
9057                        .collect();
9058                }
9059                return outcomes.to_vec();
9060            }
9061            #[cfg(feature = "perf-counters")]
9062            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
9063        }
9064
9065        // Cycle detection: clean recognition keeps the narrow static cycle
9066        // guard used on hot paths. Recovery needs the broader epsilon-state
9067        // guard because an otherwise non-nullable loop body can recover as an
9068        // empty child at EOF and re-enter the loop at the same token.
9069        let needs_cycle_guard = if self.fast_recovery_enabled {
9070            transitions.iter().any(ParserTransition::is_epsilon)
9071        } else {
9072            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
9073        };
9074        #[cfg(feature = "perf-counters")]
9075        if needs_cycle_guard {
9076            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
9077        } else {
9078            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
9079            match state
9080                .transitions()
9081                .first()
9082                .expect("single-transition path requires one transition")
9083                .data()
9084            {
9085                Transition::Rule { .. } => {
9086                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
9087                }
9088                Transition::Atom { .. }
9089                | Transition::Range { .. }
9090                | Transition::Set { .. }
9091                | Transition::NotSet { .. }
9092                | Transition::Wildcard { .. } => {
9093                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
9094                }
9095                _ => {
9096                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
9097                }
9098            }
9099        }
9100        let has_inserted_cycle_guard = if needs_cycle_guard {
9101            if !visiting.insert(key.clone()) {
9102                #[cfg(feature = "perf-counters")]
9103                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
9104                return Vec::new();
9105            }
9106            true
9107        } else {
9108            false
9109        };
9110        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9111            Some(index)
9112        } else {
9113            decision_start_index
9114        };
9115        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
9116            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
9117        } else {
9118            (Rc::clone(&recovery_symbols), recovery_state)
9119        };
9120
9121        // Lookahead-based pruning. At a multi-alternative state we cache the
9122        // look-1 set of every outgoing transition; on visit we keep only the
9123        // transitions whose look-1 can accept the current lookahead (or that
9124        // can be reached without consuming and so could legitimately match a
9125        // shorter input). This is the main speedup vs. blind speculative
9126        // recursion: it lets each visit fan out only to the alternatives that
9127        // could possibly contribute a clean parse, mirroring the SLL phase of
9128        // ANTLR's adaptive prediction.
9129        //
9130        // Pruning is skipped at:
9131        //   * rule-start states (a child rule call may need every internal
9132        //     transition to surface single-token recovery diagnostics that
9133        //     ANTLR's reference parser emits at the rule's first consuming
9134        //     transition; the FIRST-set retry path turns the prefilter off
9135        //     entirely so let's keep this lightweight too),
9136        //   * left-recursive precedence loops (the precedence transition's
9137        //     gating is dynamic),
9138        //   * states with too few alternatives to benefit.
9139        let lookahead_filter = if transition_count > 1
9140            && self.fast_first_set_prefilter
9141            && !state.precedence_rule_decision()
9142            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
9143        {
9144            state
9145                .rule_index()
9146                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
9147                .map(|rule_stop| {
9148                    let symbol = self.token_type_at(index);
9149                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
9150                    (symbol, entry)
9151                })
9152        } else {
9153            None
9154        };
9155        // LL(1) fast path: when the FIRST sets for the decision are disjoint
9156        // and none is nullable, the lookahead deterministically selects one
9157        // alternative. The recursive recognizer can then commit to that single
9158        // alt without iterating every transition through `should_skip_via_lookahead`
9159        // — saving (transition_count - 1) filter probes per visit.
9160        //
9161        // Result is cached per `(state, lookahead_token)` on the parser
9162        // instance, so subsequent visits skip the FIRST-set scan entirely.
9163        let ll1_only_alt: Option<usize> = if transition_count > 1
9164            && let Some((symbol, entry)) = lookahead_filter.as_ref()
9165        {
9166            let key = (state.state_number(), *symbol);
9167            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
9168                cached
9169            } else {
9170                let result = ll1_unique_alt(entry, *symbol);
9171                self.ll1_decision_cache.insert(key, result);
9172                result
9173            }
9174        } else {
9175            None
9176        };
9177        let lookahead_filter = lookahead_filter.as_ref();
9178        // Pre-size only when we expect at least one outcome to land — most
9179        // single-transition fall-throughs (the loop above didn't catch
9180        // because they're atom/rule/predicate) push at most one entry, so
9181        // reserving one slot avoids a reallocation while keeping the
9182        // unused-slot waste at one element.
9183        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
9184        for (transition_index, transition) in transitions.iter().enumerate() {
9185            if let Some(alt) = ll1_only_alt {
9186                // LL(1) determinism: skip every alt except the chosen one.
9187                if alt != transition_index {
9188                    continue;
9189                }
9190            }
9191            let transition_kind = transition.kind();
9192            if ll1_only_alt.is_none()
9193                && should_skip_via_lookahead(
9194                    transition_kind,
9195                    transition_index,
9196                    lookahead_filter,
9197                    index,
9198                    self.fast_recovery_enabled,
9199                    expected,
9200                )
9201            {
9202                continue;
9203            }
9204            let target = transition.target();
9205            let outcomes_before_transition = outcomes.len();
9206            let left_recursive_boundary = match transition_kind {
9207                ParserTransitionKind::Epsilon
9208                | ParserTransitionKind::Action
9209                | ParserTransitionKind::Predicate
9210                | ParserTransitionKind::Precedence => left_recursive_boundary(atn, state, target),
9211                ParserTransitionKind::Atom
9212                | ParserTransitionKind::Range
9213                | ParserTransitionKind::Set
9214                | ParserTransitionKind::NotSet
9215                | ParserTransitionKind::Wildcard
9216                | ParserTransitionKind::Rule => None,
9217            };
9218            match transition_kind {
9219                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
9220                    #[cfg(feature = "perf-counters")]
9221                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9222                    outcomes.extend(self.recognize_state_fast(
9223                        atn,
9224                        FastRecognizeRequest {
9225                            state_number: target,
9226                            stop_state,
9227                            index,
9228                            rule_start_index,
9229                            decision_start_index: next_decision_start_index,
9230                            precedence,
9231                            depth: depth + 1,
9232                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9233                            recovery_state: epsilon_recovery_state,
9234                        },
9235                        FastRecognizeScratch {
9236                            predicate_context,
9237                            visiting,
9238                            memo,
9239                            expected,
9240                            native_depth: native_depth + 1,
9241                        },
9242                    ));
9243                }
9244                ParserTransitionKind::Predicate => {
9245                    #[cfg(feature = "perf-counters")]
9246                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9247                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
9248                        outcomes.extend(self.recognize_state_fast(
9249                            atn,
9250                            FastRecognizeRequest {
9251                                state_number: target,
9252                                stop_state,
9253                                index,
9254                                rule_start_index,
9255                                decision_start_index: next_decision_start_index,
9256                                precedence,
9257                                depth: depth + 1,
9258                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9259                                recovery_state: epsilon_recovery_state,
9260                            },
9261                            FastRecognizeScratch {
9262                                predicate_context,
9263                                visiting,
9264                                memo,
9265                                expected,
9266                                native_depth: native_depth + 1,
9267                            },
9268                        ));
9269                    } else {
9270                        record_predicate_no_viable(expected, next_decision_start_index, index);
9271                    }
9272                }
9273                ParserTransitionKind::Precedence => {
9274                    let transition_precedence = packed_i32(transition.arg0());
9275                    if transition_precedence >= precedence {
9276                        outcomes.extend(self.recognize_state_fast(
9277                            atn,
9278                            FastRecognizeRequest {
9279                                state_number: target,
9280                                stop_state,
9281                                index,
9282                                rule_start_index,
9283                                decision_start_index: next_decision_start_index,
9284                                precedence,
9285                                depth: depth + 1,
9286                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9287                                recovery_state: epsilon_recovery_state,
9288                            },
9289                            FastRecognizeScratch {
9290                                predicate_context,
9291                                visiting,
9292                                memo,
9293                                expected,
9294                                native_depth: native_depth + 1,
9295                            },
9296                        ));
9297                    }
9298                }
9299                ParserTransitionKind::Rule => {
9300                    let rule_index = transition.arg0() as usize;
9301                    let follow_state = transition.arg1() as usize;
9302                    let rule_precedence = packed_i32(transition.arg2());
9303                    #[cfg(feature = "perf-counters")]
9304                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
9305                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
9306                        continue;
9307                    };
9308                    // Lookahead-based pruning. The recognizer would otherwise
9309                    // explore every speculative rule call, producing exponential
9310                    // work on grammars with many epsilon-reachable rules. When
9311                    // the rule is non-nullable and its FIRST set excludes the
9312                    // current lookahead, recursion can't find a clean path
9313                    // *through this rule*. Skipping is only safe if some sibling
9314                    // transition can still consume the lookahead — otherwise the
9315                    // rule call is the sole continuation and must run so the
9316                    // single-token insertion / deletion recovery inside the
9317                    // called rule can fire (mirroring ANTLR's reference behavior
9318                    // of conjuring a missing token at child-rule entry).
9319                    let symbol = self.token_type_at(index);
9320                    if self.fast_first_set_prefilter {
9321                        // Probe the shared cross-parse cache first; build
9322                        // the entry on miss and intern it there. The
9323                        // computation is purely a function of the ATN, so
9324                        // the cached entry is reused across parses (and
9325                        // freshly-instantiated parser values that share
9326                        // the same `&'static Atn`).
9327                        //
9328                        // `rule_first_set` returns the computed entry
9329                        // directly — it intentionally skips inserting into
9330                        // the cache when the FIRST-set walk hit a cycle, so
9331                        // we cannot assume the entry is in the cache after
9332                        // computing it.
9333                        let first = self.cached_rule_first_set(atn, target, child_stop);
9334                        if should_skip_rule_via_first_set(
9335                            &first,
9336                            symbol,
9337                            self.fast_recovery_enabled,
9338                            index,
9339                            expected,
9340                        ) {
9341                            continue;
9342                        }
9343                    }
9344                    let expected_before_child =
9345                        self.fast_recovery_enabled.then(|| expected.clone());
9346                    let mut children = self.recognize_state_fast(
9347                        atn,
9348                        FastRecognizeRequest {
9349                            state_number: target,
9350                            stop_state: child_stop,
9351                            index,
9352                            rule_start_index: index,
9353                            decision_start_index: None,
9354                            precedence: rule_precedence,
9355                            depth: depth + 1,
9356                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9357                            recovery_state: epsilon_recovery_state,
9358                        },
9359                        FastRecognizeScratch {
9360                            predicate_context,
9361                            visiting,
9362                            memo,
9363                            expected,
9364                            native_depth: native_depth + 1,
9365                        },
9366                    );
9367                    if children.is_empty() && self.fast_recovery_enabled {
9368                        children = self.fast_child_rule_failure_recovery_outcomes(
9369                            FastChildRuleFailureRecoveryRequest {
9370                                atn,
9371                                rule_index,
9372                                start_index: index,
9373                                follow_state,
9374                                stop_state,
9375                                expected,
9376                            },
9377                        );
9378                    }
9379                    if let Some(expected_before_child) = expected_before_child {
9380                        if children
9381                            .iter()
9382                            .any(|child| child.diagnostics.is_empty() && child.index > index)
9383                        {
9384                            *expected = expected_before_child;
9385                        }
9386                    }
9387                    for child in children {
9388                        let child_index = child.index;
9389                        let child_consumed_eof = child.consumed_eof;
9390                        let child_diagnostics = child.diagnostics;
9391                        let empty_recovery = self.empty_recovery_symbols();
9392                        let follow_outcomes = self.recognize_state_fast(
9393                            atn,
9394                            FastRecognizeRequest {
9395                                state_number: follow_state,
9396                                stop_state,
9397                                index: child_index,
9398                                rule_start_index,
9399                                decision_start_index: next_decision_start_index,
9400                                precedence,
9401                                depth: depth + 1,
9402                                recovery_symbols: empty_recovery,
9403                                recovery_state: None,
9404                            },
9405                            FastRecognizeScratch {
9406                                predicate_context,
9407                                visiting,
9408                                memo,
9409                                expected,
9410                                native_depth: native_depth + 1,
9411                            },
9412                        );
9413                        if follow_outcomes.is_empty() {
9414                            continue;
9415                        }
9416                        let child_stop_index =
9417                            self.rule_stop_token_index(child_index, child_consumed_eof);
9418                        let child_node = self.build_parse_trees.then(|| {
9419                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
9420                                rule_index: u32::try_from(rule_index)
9421                                    .expect("rule index fits in u32"),
9422                                invoking_state: i32::try_from(invoking_state_number(state_number))
9423                                    .expect("invoking state fits in i32"),
9424                                start_index: u32::try_from(index)
9425                                    .expect("rule start index fits in u32"),
9426                                stop_index: child_stop_index.map(|stop_index| {
9427                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
9428                                }),
9429                                deferred_children: child.deferred_nodes,
9430                                children: child.nodes,
9431                            })
9432                        });
9433                        let child_diags_empty = child_diagnostics.is_empty();
9434                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
9435                            outcome.consumed_eof |= child_consumed_eof;
9436                            // Skip the prepend dance when there's nothing to
9437                            // merge from the child — common case in pass 1.
9438                            if !child_diags_empty {
9439                                outcome.diagnostics = self
9440                                    .recognition_arena
9441                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
9442                            }
9443                            if let Some(child_node) = child_node {
9444                                outcome.deferred_nodes = self
9445                                    .recognition_arena
9446                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
9447                            }
9448                            outcome
9449                        }));
9450                    }
9451                }
9452                ParserTransitionKind::Atom
9453                | ParserTransitionKind::Range
9454                | ParserTransitionKind::Set
9455                | ParserTransitionKind::NotSet
9456                | ParserTransitionKind::Wildcard => {
9457                    #[cfg(feature = "perf-counters")]
9458                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9459                    let symbol = self.token_type_at(index);
9460                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9461                        let next_index = self.consume_index(index, symbol);
9462                        let empty_recovery = self.empty_recovery_symbols();
9463                        outcomes.extend(
9464                            self.recognize_state_fast(
9465                                atn,
9466                                FastRecognizeRequest {
9467                                    state_number: target,
9468                                    stop_state,
9469                                    index: next_index,
9470                                    rule_start_index,
9471                                    decision_start_index: next_decision_start_index,
9472                                    precedence,
9473                                    depth: depth + 1,
9474                                    recovery_symbols: empty_recovery,
9475                                    recovery_state: None,
9476                                },
9477                                FastRecognizeScratch {
9478                                    predicate_context,
9479                                    visiting,
9480                                    memo,
9481                                    expected,
9482                                    native_depth: native_depth + 1,
9483                                },
9484                            )
9485                            .into_iter()
9486                            .map(|mut outcome| {
9487                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9488                                if self.fast_token_nodes_enabled {
9489                                    let token = self.arena_token_node(index, false);
9490                                    self.defer_fast_outcome_node(&mut outcome, token);
9491                                }
9492                                outcome
9493                            }),
9494                        );
9495                    } else {
9496                        if !self.fast_recovery_enabled {
9497                            // In pass 1 there is no recovery to attempt; the
9498                            // recovery branch below would never run, and the
9499                            // `expected_symbols` computation is just there
9500                            // to gate that branch. Skipping it eliminates
9501                            // ~1× `state_expected_symbols` lookup per failed
9502                            // atom transition (≈82K on mono-statement.cs)
9503                            // for zero observable behavior change.
9504                            continue;
9505                        }
9506                        let expected_symbols = fast_recovery_expected_symbols(
9507                            self,
9508                            atn,
9509                            state.state_number(),
9510                            &recovery_symbols,
9511                        );
9512                        if expected_symbols.contains(&symbol) {
9513                            continue;
9514                        }
9515                        {
9516                            expected.record_transition(index, transition, max_token_type);
9517                            record_no_viable_if_ambiguous(
9518                                expected,
9519                                next_decision_start_index,
9520                                index,
9521                            );
9522                            outcomes.extend(self.fast_single_token_deletion_recovery(
9523                                FastRecoveryRequest {
9524                                    atn,
9525                                    transition,
9526                                    expected_symbols: Rc::clone(&expected_symbols),
9527                                    target,
9528                                    request: FastRecognizeRequest {
9529                                        state_number,
9530                                        stop_state,
9531                                        index,
9532                                        rule_start_index,
9533                                        decision_start_index,
9534                                        precedence,
9535                                        depth,
9536                                        recovery_symbols: Rc::clone(&recovery_symbols),
9537                                        recovery_state,
9538                                    },
9539                                    visiting,
9540                                    memo,
9541                                    expected,
9542                                },
9543                                predicate_context,
9544                            ));
9545                            if !state_is_left_recursive_rule(atn, state) {
9546                                outcomes.extend(self.fast_single_token_insertion_recovery(
9547                                    FastRecoveryRequest {
9548                                        atn,
9549                                        transition,
9550                                        expected_symbols: Rc::clone(&expected_symbols),
9551                                        target,
9552                                        request: FastRecognizeRequest {
9553                                            state_number,
9554                                            stop_state,
9555                                            index,
9556                                            rule_start_index,
9557                                            decision_start_index,
9558                                            precedence,
9559                                            depth,
9560                                            recovery_symbols: Rc::clone(&recovery_symbols),
9561                                            recovery_state,
9562                                        },
9563                                        visiting,
9564                                        memo,
9565                                        expected,
9566                                    },
9567                                    predicate_context,
9568                                ));
9569                            }
9570                            outcomes.extend(self.fast_current_token_deletion_recovery(
9571                                FastCurrentTokenDeletionRequest {
9572                                    atn,
9573                                    expected_symbols,
9574                                    request: FastRecognizeRequest {
9575                                        state_number,
9576                                        stop_state,
9577                                        index,
9578                                        rule_start_index,
9579                                        decision_start_index,
9580                                        precedence,
9581                                        depth,
9582                                        recovery_symbols: Rc::clone(&recovery_symbols),
9583                                        recovery_state,
9584                                    },
9585                                    visiting,
9586                                    memo,
9587                                    expected,
9588                                },
9589                                predicate_context,
9590                            ));
9591                        }
9592                    }
9593                }
9594            }
9595            let alt_number = next_alt_number(
9596                state,
9597                transition_count,
9598                transition_index,
9599                0,
9600                self.fast_track_alt_numbers,
9601            );
9602            if alt_number != 0 || left_recursive_boundary.is_some() {
9603                for outcome in &mut outcomes[outcomes_before_transition..] {
9604                    if alt_number != 0 {
9605                        self.defer_fast_outcome_alternative(outcome, alt_number);
9606                    }
9607                    if let Some(rule_index) = left_recursive_boundary {
9608                        self.defer_fast_outcome_boundary(outcome, rule_index);
9609                    }
9610                }
9611            }
9612        }
9613
9614        if has_inserted_cycle_guard {
9615            visiting.remove(&key);
9616        }
9617        if matches!(
9618            self.prediction_mode,
9619            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9620        ) && self.fast_recovery_enabled
9621        {
9622            // Without recovery enabled every outcome already has empty
9623            // diagnostics, so the discard pass is a no-op — skipping it
9624            // saves an iter+retain on each of the ~1M visits.
9625            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
9626        }
9627        if self.fast_recovery_enabled {
9628            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
9629        } else {
9630            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
9631        }
9632        // Skip memoization for single-transition states whose outcome is
9633        // unambiguous: they only get re-entered if the caller revisits the
9634        // exact same call site, which is rare since the loop above already
9635        // collapsed straight-line epsilon walks. Multi-alternative states
9636        // are where backtracking actually revisits the same coordinate, so
9637        // we still memoize there. With recovery on we keep the existing
9638        // memoization unconditionally because the recovery branch may
9639        // record diagnostics that the cache must surface to repeated
9640        // failed visits.
9641        let should_memoize = self.fast_recovery_enabled
9642            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
9643        // Apply inline pending state to each outcome before returning.
9644        // Tokens consumed inline by the loop-collapse don't appear in the
9645        // recursive recognizer's output, so we need to prepend them here.
9646        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
9647            if inline_consumed_eof {
9648                outcome.consumed_eof = true;
9649            }
9650            if !inline_consumed_tokens.is_empty() {
9651                for token_index in inline_consumed_tokens.iter().rev() {
9652                    let token = self.arena_token_node(*token_index, false);
9653                    self.defer_fast_outcome_node(&mut outcome, token);
9654                }
9655            }
9656            outcome
9657        };
9658        if should_memoize {
9659            #[cfg(feature = "perf-counters")]
9660            {
9661                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
9662                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
9663                match outcomes.len() {
9664                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9665                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9666                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9667                }
9668            }
9669            // The memo is keyed by the loop-exit `(state_number, index)` so
9670            // the inline-consumed tokens belong to *this* call's output, not
9671            // the cached result. Memoize the bare outcomes (without the
9672            // inline-pending data), then prepend the inline data on return.
9673            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
9674            memo.insert(key, Rc::clone(&stored));
9675            if inline_pending {
9676                return stored
9677                    .iter()
9678                    .copied()
9679                    .map(&mut apply_inline_pending)
9680                    .collect();
9681            }
9682            return stored.to_vec();
9683        }
9684        #[cfg(feature = "perf-counters")]
9685        match outcomes.len() {
9686            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9687            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9688            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9689        }
9690        if inline_pending {
9691            return outcomes.into_iter().map(apply_inline_pending).collect();
9692        }
9693        outcomes
9694    }
9695
9696    /// Explores single-token deletion recovery while preserving the matched
9697    /// token and skipped error token in the selected parse tree path.
9698    fn single_token_deletion_recovery(
9699        &mut self,
9700        recovery: RecoveryRequest<'_, '_>,
9701    ) -> Vec<RecognizeOutcome> {
9702        let RecoveryRequest {
9703            atn,
9704            transition,
9705            expected_symbols,
9706            target,
9707            request,
9708            visiting,
9709            memo,
9710            expected,
9711        } = recovery;
9712        let RecognizeRequest {
9713            stop_state,
9714            index,
9715            rule_start_index,
9716            decision_start_index,
9717            init_action_rules,
9718            predicates,
9719            semantics,
9720            rule_args,
9721            member_actions,
9722            return_actions,
9723            local_int_arg,
9724            member_values,
9725            return_values,
9726            rule_alt_number,
9727            track_alt_numbers,
9728            consumed_eof,
9729            precedence,
9730            depth,
9731            ..
9732        } = request;
9733        let Some((diagnostic, next_index, next_symbol)) =
9734            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
9735        else {
9736            return Vec::new();
9737        };
9738        let after_next = self.consume_index(next_index, next_symbol);
9739        self.recognize_state(
9740            atn,
9741            RecognizeRequest {
9742                state_number: target,
9743                stop_state,
9744                index: after_next,
9745                rule_start_index,
9746                decision_start_index,
9747                init_action_rules,
9748                predicates,
9749                semantics,
9750                rule_args,
9751                member_actions,
9752                return_actions,
9753                local_int_arg,
9754                member_values,
9755                return_values,
9756                rule_alt_number,
9757                track_alt_numbers,
9758                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
9759                committed_decision: false,
9760                precedence,
9761                depth: depth + 1,
9762                recovery_symbols: BTreeSet::new(),
9763                recovery_state: None,
9764            },
9765            visiting,
9766            memo,
9767            expected,
9768        )
9769        .into_iter()
9770        .map(|mut outcome| {
9771            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9772            outcome.diagnostics = self
9773                .recognition_arena
9774                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9775            let token = self.arena_token_node(next_index, false);
9776            self.arena_prepend(&mut outcome.nodes, token);
9777            let error = self.arena_token_node(index, true);
9778            self.arena_prepend(&mut outcome.nodes, error);
9779            outcome
9780        })
9781        .collect()
9782    }
9783
9784    /// Retries the current recognition state after deleting one unexpected
9785    /// token, preserving the deleted token as an error node in the parse tree.
9786    fn current_token_deletion_recovery(
9787        &mut self,
9788        recovery: CurrentTokenDeletionRequest<'_, '_>,
9789    ) -> Vec<RecognizeOutcome> {
9790        let CurrentTokenDeletionRequest {
9791            atn,
9792            expected_symbols,
9793            mut request,
9794            visiting,
9795            memo,
9796            expected,
9797        } = recovery;
9798        let error_index = request.index;
9799        if error_index == request.rule_start_index {
9800            return Vec::new();
9801        }
9802        let Some((diagnostic, next_index, skipped)) =
9803            self.current_token_deletion(error_index, &expected_symbols)
9804        else {
9805            return Vec::new();
9806        };
9807        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9808        request.index = next_index;
9809        request.committed_decision = false;
9810        request.depth += 1;
9811        request.recovery_state = None;
9812        self.recognize_state(atn, request, visiting, memo, expected)
9813            .into_iter()
9814            .map(|mut outcome| {
9815                outcome.diagnostics = self
9816                    .recognition_arena
9817                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9818                for index in skipped.iter().rev() {
9819                    let error = self.arena_token_node(*index, true);
9820                    self.arena_prepend(&mut outcome.nodes, error);
9821                }
9822                outcome
9823            })
9824            .collect()
9825    }
9826
9827    /// Falls back after deletion/insertion repairs cannot continue from a
9828    /// failed consuming transition.
9829    fn consuming_failure_fallback(
9830        &mut self,
9831        fallback: ConsumingFailureFallback<'_>,
9832        visiting: &mut BTreeSet<RecognizeKey>,
9833        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9834        expected: &mut ExpectedTokens,
9835    ) -> Vec<RecognizeOutcome> {
9836        if fallback.expected_symbols.is_empty() {
9837            return Vec::new();
9838        }
9839        if fallback.symbol == TOKEN_EOF {
9840            return self.eof_consuming_failure_fallback(fallback, expected);
9841        }
9842        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9843    }
9844
9845    /// Keeps unexpected non-EOF input visible as an error node when no repair
9846    /// path can otherwise reach the transition target.
9847    fn non_eof_consuming_failure_fallback(
9848        &mut self,
9849        fallback: ConsumingFailureFallback<'_>,
9850        visiting: &mut BTreeSet<RecognizeKey>,
9851        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9852        expected: &mut ExpectedTokens,
9853    ) -> Vec<RecognizeOutcome> {
9854        let ConsumingFailureFallback {
9855            atn,
9856            target,
9857            request,
9858            symbol,
9859            expected_symbols,
9860            decision_start_index,
9861            decision,
9862        } = fallback;
9863        let error_index = request.index;
9864        let diagnostic =
9865            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
9866        let next_index = self.consume_index(error_index, symbol);
9867        self.recognize_state(
9868            atn,
9869            RecognizeRequest {
9870                state_number: target,
9871                stop_state: request.stop_state,
9872                index: next_index,
9873                rule_start_index: request.rule_start_index,
9874                decision_start_index,
9875                init_action_rules: request.init_action_rules,
9876                predicates: request.predicates,
9877                semantics: request.semantics,
9878                rule_args: request.rule_args,
9879                member_actions: request.member_actions,
9880                return_actions: request.return_actions,
9881                local_int_arg: request.local_int_arg,
9882                member_values: request.member_values,
9883                return_values: request.return_values,
9884                rule_alt_number: request.rule_alt_number,
9885                track_alt_numbers: request.track_alt_numbers,
9886                consumed_eof: request.consumed_eof,
9887                committed_decision: false,
9888                precedence: request.precedence,
9889                depth: request.depth + 1,
9890                recovery_symbols: BTreeSet::new(),
9891                recovery_state: None,
9892            },
9893            visiting,
9894            memo,
9895            expected,
9896        )
9897        .into_iter()
9898        .map(|mut outcome| {
9899            prepend_decision(&mut outcome, decision);
9900            outcome.diagnostics = self
9901                .recognition_arena
9902                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9903            let error = self.arena_token_node(error_index, true);
9904            self.arena_prepend(&mut outcome.nodes, error);
9905            outcome
9906        })
9907        .collect()
9908    }
9909
9910    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
9911    /// behavior of unwinding instead of inserting caller tokens at EOF.
9912    fn eof_consuming_failure_fallback(
9913        &mut self,
9914        fallback: ConsumingFailureFallback<'_>,
9915        expected: &ExpectedTokens,
9916    ) -> Vec<RecognizeOutcome> {
9917        let request = fallback.request;
9918        if request.index == request.rule_start_index {
9919            return Vec::new();
9920        }
9921        let diagnostic =
9922            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
9923        let diagnostics = self
9924            .recognition_arena
9925            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
9926        vec![RecognizeOutcome {
9927            index: request.index,
9928            consumed_eof: request.consumed_eof,
9929            alt_number: request.rule_alt_number,
9930            member_values: request.member_values,
9931            return_values: request.return_values,
9932            diagnostics,
9933            decisions: Vec::new(),
9934            actions: Vec::new(),
9935            nodes: NodeSeqId::EMPTY,
9936        }]
9937    }
9938
9939    /// Explores single-token insertion recovery while adding a conjured
9940    /// missing-token error node to the selected parse tree path.
9941    fn single_token_insertion_recovery(
9942        &mut self,
9943        recovery: RecoveryRequest<'_, '_>,
9944    ) -> Vec<RecognizeOutcome> {
9945        let RecoveryRequest {
9946            atn,
9947            transition,
9948            expected_symbols,
9949            target,
9950            request,
9951            visiting,
9952            memo,
9953            expected,
9954        } = recovery;
9955        let RecognizeRequest {
9956            stop_state,
9957            index,
9958            rule_start_index,
9959            decision_start_index,
9960            init_action_rules,
9961            predicates,
9962            semantics,
9963            rule_args,
9964            member_actions,
9965            return_actions,
9966            local_int_arg,
9967            member_values,
9968            return_values,
9969            rule_alt_number,
9970            track_alt_numbers,
9971            consumed_eof,
9972            precedence,
9973            depth,
9974            ..
9975        } = request;
9976        let follow_symbols = state_expected_symbols(atn, transition.target());
9977        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
9978            transition,
9979            index,
9980            atn.max_token_type(),
9981            &expected_symbols,
9982            &follow_symbols,
9983        ) else {
9984            return Vec::new();
9985        };
9986        self.recognize_state(
9987            atn,
9988            RecognizeRequest {
9989                state_number: target,
9990                stop_state,
9991                index,
9992                rule_start_index,
9993                decision_start_index,
9994                init_action_rules,
9995                predicates,
9996                semantics,
9997                rule_args,
9998                member_actions,
9999                return_actions,
10000                local_int_arg,
10001                member_values,
10002                return_values,
10003                rule_alt_number,
10004                track_alt_numbers,
10005                consumed_eof,
10006                committed_decision: false,
10007                precedence,
10008                depth: depth + 1,
10009                recovery_symbols: BTreeSet::new(),
10010                recovery_state: None,
10011            },
10012            visiting,
10013            memo,
10014            expected,
10015        )
10016        .into_iter()
10017        .map(|mut outcome| {
10018            outcome.diagnostics = self
10019                .recognition_arena
10020                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10021            let missing = self.arena_missing_token_node(token_type, index, text.clone());
10022            self.arena_prepend(&mut outcome.nodes, missing);
10023            outcome
10024        })
10025        .collect()
10026    }
10027
10028    /// Attempts to reach `stop_state` and carries semantic actions for the
10029    /// selected parser path.
10030    #[allow(clippy::too_many_lines)]
10031    fn recognize_state(
10032        &mut self,
10033        atn: &Atn,
10034        request: RecognizeRequest<'_>,
10035        visiting: &mut BTreeSet<RecognizeKey>,
10036        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
10037        expected: &mut ExpectedTokens,
10038    ) -> Vec<RecognizeOutcome> {
10039        let request_template = request.clone();
10040        let RecognizeRequest {
10041            state_number,
10042            stop_state,
10043            index,
10044            rule_start_index,
10045            decision_start_index,
10046            init_action_rules,
10047            predicates,
10048            semantics,
10049            rule_args,
10050            member_actions,
10051            return_actions,
10052            local_int_arg,
10053            member_values,
10054            return_values,
10055            rule_alt_number,
10056            track_alt_numbers,
10057            consumed_eof,
10058            committed_decision,
10059            precedence,
10060            depth,
10061            recovery_symbols,
10062            recovery_state,
10063        } = request;
10064        if depth > RECOGNITION_DEPTH_LIMIT {
10065            return Vec::new();
10066        }
10067        if state_number == stop_state {
10068            return stop_outcome(
10069                index,
10070                consumed_eof,
10071                rule_alt_number,
10072                member_values,
10073                return_values,
10074            );
10075        }
10076        let key = RecognizeKey {
10077            state_number,
10078            stop_state,
10079            index,
10080            rule_start_index,
10081            decision_start_index,
10082            local_int_arg,
10083            member_values: member_values.clone(),
10084            return_values: return_values.clone(),
10085            rule_alt_number,
10086            track_alt_numbers,
10087            consumed_eof,
10088            committed_decision,
10089            precedence,
10090            recovery_symbols: recovery_symbols.clone(),
10091            recovery_state,
10092        };
10093        if let Some(outcomes) = memo.get(&key) {
10094            return outcomes.clone();
10095        }
10096
10097        let visit_key = key.clone();
10098        if !visiting.insert(visit_key.clone()) {
10099            return Vec::new();
10100        }
10101
10102        let Some(state) = atn.state(state_number) else {
10103            visiting.remove(&visit_key);
10104            return Vec::new();
10105        };
10106        let decision_override_generation = self.decision_override_generation;
10107        let transitions = state.transitions();
10108        let transition_count = transitions.len();
10109        let overridden_transition = if transition_count > 1
10110            && self.semantic_hooks.observes_parser_decisions()
10111        {
10112            atn.decision_to_state()
10113                .iter()
10114                .position(|candidate| candidate == state_number)
10115                .and_then(|decision| {
10116                    self.semantic_hooks
10117                        .parser_decision_override(decision, index, transition_count)
10118                })
10119                .and_then(|alternative| alternative.checked_sub(1))
10120                .filter(|alternative| *alternative < transition_count)
10121        } else {
10122            None
10123        };
10124        if overridden_transition.is_some() {
10125            self.decision_override_generation = self.decision_override_generation.wrapping_add(1);
10126        }
10127        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
10128            Some(index)
10129        } else {
10130            decision_start_index
10131        };
10132        let (epsilon_recovery_symbols, epsilon_recovery_state) =
10133            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
10134        let mut outcomes = Vec::new();
10135        for (transition_index, transition) in transitions.iter().enumerate() {
10136            if overridden_transition.is_some_and(|forced| forced != transition_index) {
10137                continue;
10138            }
10139            let transition_committed =
10140                committed_decision || overridden_transition == Some(transition_index);
10141            let mut transition_request = request_template.clone();
10142            transition_request.committed_decision = transition_committed;
10143            let decision =
10144                transition_decision(atn, state, transition_count, transition_index, predicates);
10145            let next_alt_number = next_alt_number(
10146                state,
10147                transition_count,
10148                transition_index,
10149                rule_alt_number,
10150                track_alt_numbers,
10151            );
10152            let transition_data = transition.data();
10153            match &transition_data {
10154                Transition::Epsilon { target } | Transition::Action { target, .. } => {
10155                    let action_rule_index = match &transition_data {
10156                        Transition::Action { rule_index, .. } => Some(*rule_index),
10157                        _ => None,
10158                    };
10159                    outcomes.extend(self.recognize_epsilon_or_action_step(
10160                        atn,
10161                        &transition_request,
10162                        EpsilonActionStep {
10163                            source_state: state_number,
10164                            target: *target,
10165                            action_rule_index,
10166                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
10167                            decision,
10168                            decision_start_index: next_decision_start_index,
10169                            alt_number: next_alt_number,
10170                            recovery_symbols: epsilon_recovery_symbols.clone(),
10171                            recovery_state: epsilon_recovery_state,
10172                        },
10173                        RecognizeScratch {
10174                            visiting,
10175                            memo,
10176                            expected,
10177                        },
10178                    ));
10179                }
10180                Transition::Predicate {
10181                    target,
10182                    rule_index,
10183                    pred_index,
10184                    ..
10185                } => {
10186                    let predicate = PredicateEval {
10187                        index,
10188                        rule_index: *rule_index,
10189                        pred_index: *pred_index,
10190                        predicates,
10191                        semantics,
10192                        context: None,
10193                        local_int_arg,
10194                        member_values: &member_values,
10195                    };
10196                    if self.parser_predicate_matches(predicate) {
10197                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
10198                        outcomes.extend(
10199                            self.recognize_state(
10200                                atn,
10201                                RecognizeRequest {
10202                                    state_number: *target,
10203                                    stop_state,
10204                                    index,
10205                                    rule_start_index,
10206                                    decision_start_index: next_decision_start_index,
10207                                    init_action_rules,
10208                                    predicates,
10209                                    semantics,
10210                                    rule_args,
10211                                    member_actions,
10212                                    return_actions,
10213                                    local_int_arg,
10214                                    member_values: member_values.clone(),
10215                                    return_values: return_values.clone(),
10216                                    rule_alt_number: next_alt_number,
10217                                    track_alt_numbers,
10218                                    consumed_eof,
10219                                    committed_decision: transition_committed,
10220                                    precedence,
10221                                    depth: depth + 1,
10222                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10223                                    recovery_state: epsilon_recovery_state,
10224                                },
10225                                visiting,
10226                                memo,
10227                                expected,
10228                            )
10229                            .into_iter()
10230                            .map(|mut outcome| {
10231                                prepend_decision(&mut outcome, decision);
10232                                if let Some(rule_index) = left_recursive_boundary {
10233                                    let boundary =
10234                                        self.arena_boundary_node(rule_index, next_alt_number);
10235                                    self.arena_prepend(&mut outcome.nodes, boundary);
10236                                }
10237                                outcome
10238                            }),
10239                        );
10240                    } else if let Some(message) = semantics
10241                        .and_then(|semantics| {
10242                            self.parser_semantic_ir_predicate_failure_message(
10243                                *rule_index,
10244                                *pred_index,
10245                                semantics,
10246                            )
10247                        })
10248                        .or_else(|| {
10249                            self.parser_predicate_failure_message(
10250                                *rule_index,
10251                                *pred_index,
10252                                predicates,
10253                            )
10254                        })
10255                    {
10256                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
10257                            rule_index: *rule_index,
10258                            index,
10259                            message,
10260                            member_values: member_values.clone(),
10261                            return_values: return_values.clone(),
10262                            rule_alt_number,
10263                        }));
10264                    } else {
10265                        record_predicate_no_viable(expected, next_decision_start_index, index);
10266                    }
10267                }
10268                Transition::Precedence {
10269                    target,
10270                    precedence: transition_precedence,
10271                } => {
10272                    if *transition_precedence >= precedence {
10273                        outcomes.extend(
10274                            self.recognize_state(
10275                                atn,
10276                                RecognizeRequest {
10277                                    state_number: *target,
10278                                    stop_state,
10279                                    index,
10280                                    rule_start_index,
10281                                    decision_start_index: next_decision_start_index,
10282                                    init_action_rules,
10283                                    predicates,
10284                                    semantics,
10285                                    rule_args,
10286                                    member_actions,
10287                                    return_actions,
10288                                    local_int_arg,
10289                                    member_values: member_values.clone(),
10290                                    return_values: return_values.clone(),
10291                                    rule_alt_number: next_alt_number,
10292                                    track_alt_numbers,
10293                                    consumed_eof,
10294                                    committed_decision: transition_committed,
10295                                    precedence,
10296                                    depth: depth + 1,
10297                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10298                                    recovery_state: epsilon_recovery_state,
10299                                },
10300                                visiting,
10301                                memo,
10302                                expected,
10303                            )
10304                            .into_iter()
10305                            .map(|mut outcome| {
10306                                prepend_decision(&mut outcome, decision);
10307                                outcome
10308                            }),
10309                        );
10310                    }
10311                }
10312                Transition::Rule {
10313                    target,
10314                    rule_index,
10315                    follow_state,
10316                    precedence: rule_precedence,
10317                    ..
10318                } => {
10319                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
10320                        continue;
10321                    };
10322                    let child_local_int_arg =
10323                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
10324                    let expected_before_child = expected.clone();
10325                    let children = self.recognize_state(
10326                        atn,
10327                        RecognizeRequest {
10328                            state_number: *target,
10329                            stop_state: child_stop,
10330                            index,
10331                            rule_start_index: index,
10332                            decision_start_index: None,
10333                            init_action_rules,
10334                            predicates,
10335                            semantics,
10336                            rule_args,
10337                            member_actions,
10338                            return_actions,
10339                            local_int_arg: child_local_int_arg,
10340                            member_values: member_values.clone(),
10341                            return_values: BTreeMap::new(),
10342                            rule_alt_number: 0,
10343                            track_alt_numbers,
10344                            consumed_eof: false,
10345                            committed_decision: transition_committed,
10346                            precedence: *rule_precedence,
10347                            depth: depth + 1,
10348                            recovery_symbols: epsilon_recovery_symbols.clone(),
10349                            recovery_state: epsilon_recovery_state,
10350                        },
10351                        visiting,
10352                        memo,
10353                        expected,
10354                    );
10355                    let children = if children.is_empty() {
10356                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
10357                            atn,
10358                            rule_index: *rule_index,
10359                            start_index: index,
10360                            follow_state: *follow_state,
10361                            stop_state,
10362                            member_values: member_values.clone(),
10363                            expected,
10364                        })
10365                    } else {
10366                        children
10367                    };
10368                    let preserve_child_expected =
10369                        self.child_expected_reaches_clean_eof(&children, expected);
10370                    restore_expected(
10371                        &children,
10372                        index,
10373                        expected,
10374                        expected_before_child,
10375                        preserve_child_expected,
10376                    );
10377                    for child in children {
10378                        let child_stop_index =
10379                            self.rule_stop_token_index(child.index, child.consumed_eof);
10380                        let child_nodes = self
10381                            .recognition_arena
10382                            .fold_left_recursive_boundaries(child.nodes);
10383                        let child_node = self.arena_rule_node(ArenaRuleSpec {
10384                            rule_index: *rule_index,
10385                            invoking_state: invoking_state_number(state_number),
10386                            alt_number: child.alt_number,
10387                            start_index: index,
10388                            stop_index: child_stop_index,
10389                            return_values: child.return_values.clone(),
10390                            children: child_nodes,
10391                        });
10392                        outcomes.extend(
10393                            self.recognize_state(
10394                                atn,
10395                                RecognizeRequest {
10396                                    state_number: *follow_state,
10397                                    stop_state,
10398                                    index: child.index,
10399                                    rule_start_index,
10400                                    decision_start_index: next_decision_start_index,
10401                                    init_action_rules,
10402                                    predicates,
10403                                    semantics,
10404                                    rule_args,
10405                                    member_actions,
10406                                    return_actions,
10407                                    local_int_arg,
10408                                    member_values: child.member_values.clone(),
10409                                    return_values: return_values.clone(),
10410                                    rule_alt_number,
10411                                    track_alt_numbers,
10412                                    consumed_eof: consumed_eof || child.consumed_eof,
10413                                    committed_decision: transition_committed
10414                                        && child.index == index,
10415                                    precedence,
10416                                    depth: depth + 1,
10417                                    recovery_symbols: BTreeSet::new(),
10418                                    recovery_state: None,
10419                                },
10420                                visiting,
10421                                memo,
10422                                expected,
10423                            )
10424                            .into_iter()
10425                            .map(|mut outcome| {
10426                                outcome.consumed_eof |= child.consumed_eof;
10427                                outcome.diagnostics = self
10428                                    .recognition_arena
10429                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
10430                                let mut decisions = child.decisions.clone();
10431                                decisions.append(&mut outcome.decisions);
10432                                outcome.decisions = decisions;
10433                                prepend_decision(&mut outcome, decision);
10434                                let mut actions = child.actions.clone();
10435                                if init_action_rules.contains(rule_index) {
10436                                    actions.insert(
10437                                        0,
10438                                        ParserAction::new_rule_init(
10439                                            *rule_index,
10440                                            index,
10441                                            Some(*follow_state),
10442                                        ),
10443                                    );
10444                                }
10445                                actions.append(&mut outcome.actions);
10446                                outcome.actions = actions;
10447                                self.arena_prepend(&mut outcome.nodes, child_node);
10448                                outcome
10449                            }),
10450                        );
10451                    }
10452                }
10453                Transition::Atom { target, .. }
10454                | Transition::Range { target, .. }
10455                | Transition::Set { target, .. }
10456                | Transition::NotSet { target, .. }
10457                | Transition::Wildcard { target, .. } => {
10458                    let symbol = self.token_type_at(index);
10459                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
10460                        let next_index = self.consume_index(index, symbol);
10461                        outcomes.extend(
10462                            self.recognize_state(
10463                                atn,
10464                                RecognizeRequest {
10465                                    state_number: *target,
10466                                    stop_state,
10467                                    index: next_index,
10468                                    rule_start_index,
10469                                    decision_start_index: next_decision_start_index,
10470                                    init_action_rules,
10471                                    predicates,
10472                                    semantics,
10473                                    rule_args,
10474                                    member_actions,
10475                                    return_actions,
10476                                    local_int_arg,
10477                                    member_values: member_values.clone(),
10478                                    return_values: return_values.clone(),
10479                                    rule_alt_number: next_alt_number,
10480                                    track_alt_numbers,
10481                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
10482                                    committed_decision: false,
10483                                    precedence,
10484                                    depth: depth + 1,
10485                                    recovery_symbols: BTreeSet::new(),
10486                                    recovery_state: None,
10487                                },
10488                                visiting,
10489                                memo,
10490                                expected,
10491                            )
10492                            .into_iter()
10493                            .map(|mut outcome| {
10494                                prepend_decision(&mut outcome, decision);
10495                                outcome.consumed_eof |= symbol == TOKEN_EOF;
10496                                let token = self.arena_token_node(index, false);
10497                                self.arena_prepend(&mut outcome.nodes, token);
10498                                outcome
10499                            }),
10500                        );
10501                    } else {
10502                        let expected_symbols =
10503                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
10504                        if expected_symbols.contains(&symbol) && !transition_committed {
10505                            continue;
10506                        }
10507                        expected.record_transition(index, transition, atn.max_token_type());
10508                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
10509                        let before_recovery = outcomes.len();
10510                        let recovery_request = transition_request.clone();
10511                        if transition_committed {
10512                            outcomes.extend(self.consuming_failure_fallback(
10513                                ConsumingFailureFallback {
10514                                    atn,
10515                                    target: *target,
10516                                    request: recovery_request,
10517                                    symbol,
10518                                    expected_symbols,
10519                                    decision_start_index: next_decision_start_index,
10520                                    decision,
10521                                },
10522                                visiting,
10523                                memo,
10524                                expected,
10525                            ));
10526                            break;
10527                        }
10528                        outcomes.extend(
10529                            self.single_token_deletion_recovery(RecoveryRequest {
10530                                atn,
10531                                transition,
10532                                expected_symbols: expected_symbols.clone(),
10533                                target: *target,
10534                                request: recovery_request.clone(),
10535                                visiting,
10536                                memo,
10537                                expected,
10538                            })
10539                            .into_iter()
10540                            .map(|mut outcome| {
10541                                prepend_decision(&mut outcome, decision);
10542                                outcome
10543                            }),
10544                        );
10545                        if !state_is_left_recursive_rule(atn, state) {
10546                            outcomes.extend(
10547                                self.single_token_insertion_recovery(RecoveryRequest {
10548                                    atn,
10549                                    transition,
10550                                    expected_symbols: expected_symbols.clone(),
10551                                    target: *target,
10552                                    request: recovery_request.clone(),
10553                                    visiting,
10554                                    memo,
10555                                    expected,
10556                                })
10557                                .into_iter()
10558                                .map(|mut outcome| {
10559                                    prepend_decision(&mut outcome, decision);
10560                                    outcome
10561                                }),
10562                            );
10563                        }
10564                        outcomes.extend(self.current_token_deletion_recovery(
10565                            CurrentTokenDeletionRequest {
10566                                atn,
10567                                expected_symbols: expected_symbols.clone(),
10568                                request: recovery_request.clone(),
10569                                visiting,
10570                                memo,
10571                                expected,
10572                            },
10573                        ));
10574                        if outcomes.len() == before_recovery {
10575                            outcomes.extend(self.consuming_failure_fallback(
10576                                ConsumingFailureFallback {
10577                                    atn,
10578                                    target: *target,
10579                                    request: recovery_request,
10580                                    symbol,
10581                                    expected_symbols,
10582                                    decision_start_index: next_decision_start_index,
10583                                    decision,
10584                                },
10585                                visiting,
10586                                memo,
10587                                expected,
10588                            ));
10589                        }
10590                    }
10591                }
10592            }
10593            if self.decision_override_generation != decision_override_generation {
10594                break;
10595            }
10596        }
10597
10598        visiting.remove(&visit_key);
10599        self.record_prediction_diagnostics(atn, state, index, &outcomes);
10600        if matches!(
10601            self.prediction_mode,
10602            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
10603        ) {
10604            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
10605        }
10606        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
10607        memo.insert(key, outcomes.clone());
10608        outcomes
10609    }
10610
10611    /// Follows an epsilon or semantic-action transition while preserving the
10612    /// path-local side effects that may later become generated action output.
10613    fn recognize_epsilon_or_action_step(
10614        &mut self,
10615        atn: &Atn,
10616        request: &RecognizeRequest<'_>,
10617        step: EpsilonActionStep,
10618        scratch: RecognizeScratch<'_>,
10619    ) -> Vec<RecognizeOutcome> {
10620        let RecognizeScratch {
10621            visiting,
10622            memo,
10623            expected,
10624        } = scratch;
10625        let action = step.action_rule_index.map(|rule_index| {
10626            ParserAction::new(
10627                step.source_state,
10628                rule_index,
10629                request.rule_start_index,
10630                self.rule_stop_token_index(request.index, request.consumed_eof),
10631            )
10632        });
10633        let next_member_values = if action.is_some() {
10634            member_values_after_action(
10635                step.source_state,
10636                request.member_actions,
10637                request.semantics,
10638                &request.member_values,
10639            )
10640        } else {
10641            request.member_values.clone()
10642        };
10643        let next_return_values = action.map_or_else(
10644            || request.return_values.clone(),
10645            |action| {
10646                return_values_after_action(
10647                    step.source_state,
10648                    action.rule_index(),
10649                    request.return_actions,
10650                    request.semantics,
10651                    &request.return_values,
10652                )
10653            },
10654        );
10655
10656        self.recognize_state(
10657            atn,
10658            RecognizeRequest {
10659                state_number: step.target,
10660                stop_state: request.stop_state,
10661                index: request.index,
10662                rule_start_index: request.rule_start_index,
10663                decision_start_index: step.decision_start_index,
10664                init_action_rules: request.init_action_rules,
10665                predicates: request.predicates,
10666                semantics: request.semantics,
10667                rule_args: request.rule_args,
10668                member_actions: request.member_actions,
10669                return_actions: request.return_actions,
10670                local_int_arg: request.local_int_arg,
10671                member_values: next_member_values,
10672                return_values: next_return_values,
10673                rule_alt_number: if step.left_recursive_boundary.is_some() {
10674                    0
10675                } else {
10676                    step.alt_number
10677                },
10678                track_alt_numbers: request.track_alt_numbers,
10679                consumed_eof: request.consumed_eof,
10680                committed_decision: request.committed_decision,
10681                precedence: request.precedence,
10682                depth: request.depth + 1,
10683                recovery_symbols: step.recovery_symbols,
10684                recovery_state: step.recovery_state,
10685            },
10686            visiting,
10687            memo,
10688            expected,
10689        )
10690        .into_iter()
10691        .map(|mut outcome| {
10692            prepend_decision(&mut outcome, step.decision);
10693            if let Some(rule_index) = step.left_recursive_boundary {
10694                let boundary = self.arena_boundary_node(rule_index, step.alt_number);
10695                self.arena_prepend(&mut outcome.nodes, boundary);
10696            }
10697            if let Some(action) = action {
10698                outcome.actions.insert(0, action);
10699            }
10700            outcome
10701        })
10702        .collect()
10703    }
10704
10705    /// Reads the token type at an absolute token-stream index without moving
10706    /// the parser's stream cursor. The fast recognizer probes lookahead at
10707    /// every state visit, so avoiding the seek round-trip is a measurable
10708    /// hot-path win on long inputs.
10709    fn token_type_at(&mut self, index: usize) -> i32 {
10710        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
10711            self.input.fill();
10712        }
10713        self.input.token_type_at_index(index)
10714    }
10715
10716    /// Returns the cached `state_expected_symbols` set for an ATN state.
10717    ///
10718    /// The fast recognizer consults this set on every state visit through
10719    /// `next_recovery_context`; the underlying DFS is a pure function of the
10720    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
10721    ///
10722    /// Caching is layered through `intern_recovery_symbols` so two ATN states
10723    /// with the same expected-symbol set share one `Rc`. That invariant is
10724    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
10725    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
10726    /// always interned before it ends up in a key.
10727    fn cached_state_expected_symbols(
10728        &mut self,
10729        atn: &Atn,
10730        state_number: usize,
10731    ) -> Rc<BTreeSet<i32>> {
10732        if let Some(cached) = self.state_expected_cache.get(&state_number) {
10733            return Rc::clone(cached);
10734        }
10735        let symbols = state_expected_symbols(atn, state_number);
10736        let entry = self.intern_recovery_symbols(symbols);
10737        self.state_expected_cache
10738            .insert(state_number, Rc::clone(&entry));
10739        entry
10740    }
10741
10742    fn cached_state_expected_token_set(
10743        &mut self,
10744        atn: &Atn,
10745        state_number: usize,
10746    ) -> Rc<TokenBitSet> {
10747        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
10748            return Rc::clone(cached);
10749        }
10750        // Purely a function of the ATN, so back the per-parser cache with the
10751        // thread-shared one — fresh parser instances (one per parse in
10752        // generated usage) start warm instead of rewalking the ATN.
10753        let symbols = with_shared_atn_caches(atn, |cache| {
10754            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
10755                return Rc::clone(cached);
10756            }
10757            let symbols = Rc::new(state_expected_token_set(atn, state_number));
10758            cache
10759                .state_expected_tokens
10760                .insert(state_number, Rc::clone(&symbols));
10761            symbols
10762        });
10763        self.state_expected_token_cache
10764            .insert(state_number, Rc::clone(&symbols));
10765        symbols
10766    }
10767
10768    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
10769        if self.rule_stop_reach_cache.len() <= state_number {
10770            self.rule_stop_reach_cache
10771                .resize_with(atn.states().len().max(state_number + 1), || None);
10772        }
10773        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
10774            return reaches;
10775        }
10776        let reaches = with_shared_atn_caches(atn, |cache| {
10777            *cache
10778                .rule_stop_reach
10779                .entry(state_number)
10780                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
10781        });
10782        self.rule_stop_reach_cache[state_number] = Some(reaches);
10783        reaches
10784    }
10785
10786    /// Returns the parser's empty `recovery_symbols` singleton so callers can
10787    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
10788    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
10789        Rc::clone(&self.empty_recovery_symbols)
10790    }
10791
10792    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
10793    /// recognizer can hash and compare keys by pointer.
10794    ///
10795    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
10796    /// come from this method or the empty singleton; otherwise two
10797    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
10798    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
10799    /// recognition coordinates.
10800    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
10801        if set.is_empty() {
10802            return Rc::clone(&self.empty_recovery_symbols);
10803        }
10804        let candidate = Rc::new(set);
10805        match self.recovery_symbols_intern.get(&candidate) {
10806            Some(existing) => Rc::clone(existing),
10807            None => {
10808                self.recovery_symbols_intern
10809                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
10810                candidate
10811            }
10812        }
10813    }
10814
10815    /// Returns the cached look-1 entry for a decision state, computing it on
10816    /// first use. Multi-alternative states are visited many times during
10817    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
10818    /// hash lookup per visit.
10819    fn cached_decision_lookahead(
10820        &mut self,
10821        atn: &Atn,
10822        state: AtnState<'_>,
10823        rule_stop_state: usize,
10824    ) -> Rc<DecisionLookahead> {
10825        // Hit the parser-instance cache first. Decision lookahead is purely
10826        // a function of the ATN/state, so on a warm cache we skip the
10827        // thread-local + RefCell + HashMap-entry dance through
10828        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
10829        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
10830        // hashmap-entry overhead in profiles.
10831        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
10832            return Rc::clone(cached);
10833        }
10834        let entry = with_shared_atn_caches(atn, |cache| {
10835            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10836                return Rc::clone(cached);
10837            }
10838            let mut entry = DecisionLookahead {
10839                transitions: Vec::with_capacity(state.transitions().len()),
10840            };
10841            for transition in &state.transitions() {
10842                entry.transitions.push(transition_first_set(
10843                    atn,
10844                    transition,
10845                    rule_stop_state,
10846                    &mut cache.first_set,
10847                ));
10848            }
10849            let entry = Rc::new(entry);
10850            cache
10851                .decision_lookahead
10852                .insert(state.state_number(), Rc::clone(&entry));
10853            entry
10854        });
10855        self.decision_lookahead_cache
10856            .insert(state.state_number(), Rc::clone(&entry));
10857        entry
10858    }
10859
10860    fn cached_rule_first_set(
10861        &mut self,
10862        atn: &Atn,
10863        target: usize,
10864        child_stop: usize,
10865    ) -> Rc<FirstSet> {
10866        if self.rule_first_set_cache.len() <= target {
10867            self.rule_first_set_cache
10868                .resize_with(atn.states().len().max(target + 1), || None);
10869        }
10870        if let Some(cached) = self
10871            .rule_first_set_cache
10872            .get(target)
10873            .and_then(Option::as_ref)
10874        {
10875            return Rc::clone(cached);
10876        }
10877        let first = with_shared_first_set_cache(atn, |cache| {
10878            rule_first_set(atn, target, child_stop, cache)
10879        });
10880        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
10881        first
10882    }
10883
10884    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
10885        let atn_key = SharedAtnCacheKey::for_atn(atn);
10886        if self.empty_cycle_cache_atn != Some(atn_key) {
10887            self.empty_cycle_cache.clear();
10888            self.empty_cycle_cache_atn = Some(atn_key);
10889        }
10890        if self.empty_cycle_cache.len() <= state_number {
10891            self.empty_cycle_cache
10892                .resize_with(atn.state_count().max(state_number + 1), || None);
10893        }
10894        if let Some(cached) = self.empty_cycle_cache[state_number] {
10895            return cached;
10896        }
10897        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
10898        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
10899        self.empty_cycle_cache[state_number] = Some(result);
10900        result
10901    }
10902
10903    fn empty_path_reaches_state(
10904        &mut self,
10905        atn: &Atn,
10906        state_number: usize,
10907        target_state: usize,
10908        visited: &mut FxHashSet<usize>,
10909    ) -> bool {
10910        enum Work {
10911            Visit(usize),
10912            RuleFollow {
10913                target: usize,
10914                rule_index: usize,
10915                follow_state: usize,
10916            },
10917        }
10918
10919        let mut work = vec![Work::Visit(state_number)];
10920        while let Some(item) = work.pop() {
10921            match item {
10922                Work::Visit(state_number) => {
10923                    if !visited.insert(state_number) {
10924                        continue;
10925                    }
10926                    let Some(state) = atn.state(state_number) else {
10927                        continue;
10928                    };
10929                    let transitions = state.transitions();
10930                    for transition_index in (0..transitions.len()).rev() {
10931                        let transition = transitions
10932                            .get(transition_index)
10933                            .expect("in-bounds parser transition");
10934                        let kind = transition.kind();
10935                        let target = transition.target();
10936                        match kind {
10937                            ParserTransitionKind::Atom
10938                            | ParserTransitionKind::Range
10939                            | ParserTransitionKind::Set
10940                            | ParserTransitionKind::NotSet
10941                            | ParserTransitionKind::Wildcard => {}
10942                            ParserTransitionKind::Rule => {
10943                                if target == target_state {
10944                                    return true;
10945                                }
10946                                work.push(Work::RuleFollow {
10947                                    target,
10948                                    rule_index: transition.arg0() as usize,
10949                                    follow_state: transition.arg1() as usize,
10950                                });
10951                                work.push(Work::Visit(target));
10952                            }
10953                            ParserTransitionKind::Epsilon
10954                            | ParserTransitionKind::Predicate
10955                            | ParserTransitionKind::Action
10956                            | ParserTransitionKind::Precedence => {
10957                                if target == target_state {
10958                                    return true;
10959                                }
10960                                work.push(Work::Visit(target));
10961                            }
10962                        }
10963                    }
10964                }
10965                Work::RuleFollow {
10966                    target,
10967                    rule_index,
10968                    follow_state,
10969                } => {
10970                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
10971                        continue;
10972                    };
10973                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
10974                        if follow_state == target_state {
10975                            return true;
10976                        }
10977                        work.push(Work::Visit(follow_state));
10978                    }
10979                }
10980            }
10981        }
10982        false
10983    }
10984
10985    /// Decides whether the clean recognizer should use its full outcome memo
10986    /// table for this coordinate.
10987    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
10988        match self.clean_memo_mode {
10989            CleanMemoMode::Promote => true,
10990            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
10991            CleanMemoMode::Sparse => {
10992                self.clean_memo_sparse_samples += 1;
10993                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
10994                    return false;
10995                }
10996                self.clean_memo_sparse_samples = 0;
10997                self.clean_memo_mode = CleanMemoMode::Probe;
10998                self.clean_memo_probe_samples = 0;
10999                self.clean_memo_probe_repeats = 0;
11000                self.clean_memo_probe_seen.clear();
11001                self.observe_clean_memo_probe(key)
11002            }
11003        }
11004    }
11005
11006    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
11007        self.clean_memo_probe_samples += 1;
11008        if !self.clean_memo_probe_seen.insert(key.clone()) {
11009            self.clean_memo_probe_repeats += 1;
11010        }
11011        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
11012            self.clean_memo_mode = CleanMemoMode::Promote;
11013            self.clean_memo_probe_seen.clear();
11014            return true;
11015        }
11016        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
11017            self.clean_memo_mode = CleanMemoMode::Sparse;
11018            self.clean_memo_sparse_samples = 0;
11019            self.clean_memo_probe_seen.clear();
11020            return false;
11021        }
11022        true
11023    }
11024
11025    /// Borrows the visible token at an absolute token-stream index.
11026    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
11027        self.input.get(index)
11028    }
11029
11030    /// Returns the compact token ID at an absolute token-stream index.
11031    fn token_id_at(&self, index: usize) -> Option<TokenId> {
11032        self.input.get_id(index)
11033    }
11034
11035    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
11036        let token = self
11037            .token_id_at(index)
11038            .expect("recognized token index must exist in the token store");
11039        let node = if error {
11040            ArenaRecognizedNode::ErrorToken { token }
11041        } else {
11042            ArenaRecognizedNode::Token { token }
11043        };
11044        self.recognition_arena.push_node(node)
11045    }
11046
11047    fn arena_missing_token_node(
11048        &mut self,
11049        token_type: i32,
11050        at_index: usize,
11051        text: String,
11052    ) -> RecognizedNodeId {
11053        let extra = self
11054            .recognition_arena
11055            .push_extra(RecognitionExtra::MissingToken {
11056                token_type,
11057                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
11058                text,
11059            });
11060        self.recognition_arena
11061            .push_node(ArenaRecognizedNode::MissingToken { extra })
11062    }
11063
11064    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
11065        let ArenaRuleSpec {
11066            rule_index,
11067            invoking_state,
11068            alt_number,
11069            start_index,
11070            stop_index,
11071            return_values,
11072            children,
11073        } = spec;
11074        let return_values = (!return_values.is_empty()).then(|| {
11075            self.recognition_arena
11076                .push_extra(RecognitionExtra::ReturnValues(return_values))
11077        });
11078        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
11079            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11080            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
11081            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11082            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
11083            stop_index: stop_index
11084                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
11085            return_values,
11086            children,
11087        })
11088    }
11089
11090    fn arena_boundary_node(&mut self, rule_index: usize, alt_number: usize) -> RecognizedNodeId {
11091        self.recognition_arena
11092            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
11093                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11094                alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11095            })
11096    }
11097
11098    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
11099        *sequence = self.recognition_arena.prepend(*sequence, node);
11100    }
11101
11102    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
11103        self.last_recognition_arena_root = root;
11104        self.last_recognition_arena_diagnostics = diagnostics;
11105        #[cfg(feature = "perf-counters")]
11106        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
11107            let stats = self.recognition_arena_stats();
11108            #[allow(clippy::print_stderr)]
11109            {
11110                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
11111                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
11112                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
11113                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
11114                eprintln!("perf recognition_links_total={}", stats.total_links);
11115                eprintln!("perf recognition_links_live={}", stats.live_links);
11116                eprintln!("perf recognition_links_dead={}", stats.dead_links);
11117                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
11118                eprintln!("perf recognition_extras_total={}", stats.total_extras);
11119                eprintln!("perf recognition_extras_live={}", stats.live_extras);
11120                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
11121                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
11122            }
11123        }
11124    }
11125
11126    fn reset_recognition_arena(&mut self) {
11127        self.recognition_arena.reset();
11128        self.last_recognition_arena_root = NodeSeqId::EMPTY;
11129        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
11130    }
11131
11132    /// Normalizes the current token-stream cursor to the next parser-visible
11133    /// token before capturing a rule start boundary.
11134    fn current_visible_index(&mut self) -> usize {
11135        let index = self.input.index();
11136        self.input.seek(index);
11137        self.input.index()
11138    }
11139
11140    /// Reports whether a child rule reached EOF cleanly while also recording
11141    /// an EOF expectation from a longer path inside that child.
11142    fn child_expected_reaches_clean_eof(
11143        &mut self,
11144        children: &[RecognizeOutcome],
11145        expected: &ExpectedTokens,
11146    ) -> bool {
11147        let Some(index) = expected.index else {
11148            return false;
11149        };
11150        self.token_type_at(index) == TOKEN_EOF
11151            && children
11152                .iter()
11153                .any(|child| child.diagnostics.is_empty() && child.index == index)
11154    }
11155
11156    /// Finds the previous token visible to the parser before `index`.
11157    ///
11158    /// The token stream cursor skips hidden-channel tokens, so subtracting one
11159    /// from a visible-token index can point at whitespace. Parser intervals use
11160    /// this helper to stop at the previous visible token while preserving hidden
11161    /// text inside the rendered interval.
11162    fn previous_token_index(&self, index: usize) -> Option<usize> {
11163        self.input.previous_visible_token_index(index)
11164    }
11165
11166    /// Returns the token-stream index used as a rule stop boundary.
11167    ///
11168    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
11169    /// stop at `index` rather than at the previous visible token.
11170    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
11171        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
11172            Some(index)
11173        } else {
11174            self.previous_token_index(index)
11175        }
11176    }
11177
11178    /// Stop-token index for a rule's `@after` action, matching the boundary that
11179    /// `finish_rule` records on the rule context.
11180    ///
11181    /// A rule that matched EOF leaves the cursor parked on the EOF token
11182    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
11183    /// the current index rather than the previous visible token. Without this,
11184    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
11185    /// report the token before EOF (or `None` for empty input), diverging from
11186    /// the rule context that `finish_rule` builds.
11187    ///
11188    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
11189    /// rule ends right before EOF without matching it (the cursor is parked on
11190    /// EOF, but the rule did not consume it). Prefer
11191    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
11192    /// rule context already recorded with the real flag. Kept for callers without
11193    /// the rule tree in hand.
11194    #[must_use]
11195    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
11196        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
11197        self.rule_stop_token_index(current_index, consumed_eof)
11198    }
11199
11200    /// Stop-token index for a rule's `@after` action, taken from the stop token
11201    /// the rule context already recorded.
11202    ///
11203    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
11204    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
11205    /// the rule context — even when the rule ends immediately before EOF without
11206    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
11207    /// to the cursor-based inference only when the tree carries no rule stop.
11208    #[must_use]
11209    pub fn after_action_stop_index_for_tree(
11210        &mut self,
11211        tree: ParseTree,
11212        current_index: usize,
11213    ) -> Option<usize> {
11214        if let Some(stop) = self
11215            .node(tree)
11216            .as_rule()
11217            .and_then(crate::tree::RuleNodeView::stop_id)
11218        {
11219            return Some(stop.index());
11220        }
11221        self.after_action_stop_index(current_index)
11222    }
11223
11224    /// Start-token index for a rule's `@after` action, taken from the start token
11225    /// the rule context already recorded.
11226    ///
11227    /// `enter_rule` sets the rule context start to the first visible token (it
11228    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
11229    /// `$text` in an `@after` action aligned with the rule context — even when the
11230    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
11231    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
11232    /// the tree carries no rule start.
11233    #[must_use]
11234    pub fn after_action_start_index_for_tree(
11235        &self,
11236        tree: ParseTree,
11237        fallback_index: usize,
11238    ) -> usize {
11239        if let Some(start) = self
11240            .node(tree)
11241            .as_rule()
11242            .and_then(crate::tree::RuleNodeView::start_id)
11243        {
11244            return start.index();
11245        }
11246        fallback_index
11247    }
11248
11249    /// Returns the rule stop token for a selected parse path.
11250    ///
11251    /// EOF transitions do not advance the token-stream cursor, so an EOF match
11252    /// must use the current token rather than the previous visible token.
11253    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
11254        self.rule_stop_token_index(index, consumed_eof)
11255            .and_then(|token_index| self.token_id_at(token_index))
11256    }
11257
11258    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
11259    ///
11260    /// Generated Java reports the failed-predicate message at the current
11261    /// lookahead, then consumes until rule recovery can resume. The metadata
11262    /// runtime models the same visible tree shape by keeping skipped tokens as
11263    /// error nodes and returning from the active rule at EOF.
11264    fn predicate_failure_recovery(
11265        &mut self,
11266        request: PredicateFailureRecovery<'_>,
11267    ) -> RecognizeOutcome {
11268        let PredicateFailureRecovery {
11269            rule_index,
11270            index,
11271            message,
11272            member_values,
11273            return_values,
11274            rule_alt_number,
11275        } = request;
11276        let rule_name = self
11277            .rule_names()
11278            .get(rule_index)
11279            .map_or_else(|| rule_index.to_string(), Clone::clone);
11280        let diagnostic = diagnostic_for_token(
11281            self.token_at(index).as_ref(),
11282            format!("rule {rule_name} {message}"),
11283        );
11284        let mut reversed_nodes = NodeSeqId::EMPTY;
11285        let mut next_index = index;
11286        loop {
11287            let symbol = self.token_type_at(next_index);
11288            if symbol == TOKEN_EOF {
11289                break;
11290            }
11291            let error = self.arena_token_node(next_index, true);
11292            self.arena_prepend(&mut reversed_nodes, error);
11293            let after = self.consume_index(next_index, symbol);
11294            if after == next_index {
11295                break;
11296            }
11297            next_index = after;
11298        }
11299        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
11300        let diagnostics = self
11301            .recognition_arena
11302            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
11303        RecognizeOutcome {
11304            index: next_index,
11305            consumed_eof: false,
11306            alt_number: rule_alt_number,
11307            member_values,
11308            return_values,
11309            diagnostics,
11310            decisions: Vec::new(),
11311            actions: Vec::new(),
11312            nodes,
11313        }
11314    }
11315
11316    /// Evaluates a user hook for a predicate coordinate that has no generated
11317    /// runtime table entry.
11318    fn parser_semantic_hook_result(
11319        &mut self,
11320        request: ParserSemanticHookRequest<'_>,
11321    ) -> Option<bool> {
11322        let ParserSemanticHookRequest {
11323            index,
11324            rule_index,
11325            pred_index,
11326            context,
11327            local_int_arg,
11328            member_values,
11329        } = request;
11330        let rule_name = self.rule_names().get(rule_index).cloned();
11331        self.input.seek(index);
11332        let input = &mut self.input;
11333        let semantic_hooks = &mut self.semantic_hooks;
11334        let mut ctx = ParserSemCtx {
11335            input,
11336            tree_storage: &self.tree,
11337            rule_index,
11338            coordinate_index: pred_index,
11339            rule_name,
11340            context,
11341            tree: None,
11342            local_int_arg,
11343            member_values,
11344            action: None,
11345        };
11346        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
11347    }
11348
11349    /// Re-inserts unknown-predicate coordinates recorded before a nested
11350    /// interpreted recognition, preserving order and skipping any the nested
11351    /// call already recorded, so a generated parent's fail-loud coordinates
11352    /// survive descending into an interpreted child.
11353    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
11354        if prior.is_empty() {
11355            return;
11356        }
11357        let mut merged = prior;
11358        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
11359            if !merged.contains(&coordinate) {
11360                merged.push(coordinate);
11361            }
11362        }
11363        self.unknown_predicate_hits = merged;
11364    }
11365
11366    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
11367    /// that has no entry in the generated predicate table.
11368    ///
11369    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
11370    /// the guarded path is abandoned; the parse entry surfaces the recorded
11371    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
11372    /// because a parse that consulted an unknown predicate is unreliable no
11373    /// matter which paths were ultimately selected.
11374    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
11375        apply_unknown_predicate_policy(
11376            self.unknown_predicate_policy,
11377            rule_index,
11378            pred_index,
11379            &mut self.unknown_predicate_hits,
11380        )
11381    }
11382
11383    /// Builds the fail-loud error for unknown predicate coordinates recorded
11384    /// by the current parse, if any.
11385    fn unknown_semantic_error(&self) -> Option<AntlrError> {
11386        use std::fmt::Write as _;
11387        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
11388            return None;
11389        }
11390        let mut message = String::new();
11391        for (rule_index, pred_index) in &self.unknown_predicate_hits {
11392            if !message.is_empty() {
11393                message.push_str("; ");
11394            }
11395            let _ = match self.rule_names().get(*rule_index) {
11396                Some(rule_name) => write!(
11397                    message,
11398                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
11399                ),
11400                None => write!(
11401                    message,
11402                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
11403                ),
11404            };
11405        }
11406        for (rule_index, source_state) in &self.unhandled_action_hits {
11407            if !message.is_empty() {
11408                message.push_str("; ");
11409            }
11410            let _ = match self.rule_names().get(*rule_index) {
11411                Some(rule_name) => write!(
11412                    message,
11413                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
11414                ),
11415                None => write!(
11416                    message,
11417                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
11418                ),
11419            };
11420        }
11421        Some(AntlrError::Unsupported(message))
11422    }
11423
11424    /// Evaluates one lowered predicate expression at the requested input
11425    /// position.
11426    ///
11427    /// This sits in the prediction hot loop, so the context borrows the
11428    /// speculative member state read-only and the rule name by reference —
11429    /// no per-evaluation allocation. Only the hook escape path materializes
11430    /// owned copies, and only when a hook is actually consulted.
11431    fn parser_semir_predicate_matches(
11432        &mut self,
11433        semantics: &ParserSemantics,
11434        predicate: &ParserSemanticPredicate,
11435        request: ParserSemanticHookRequest<'_>,
11436    ) -> bool {
11437        self.input.seek(request.index);
11438        let rule_name = self
11439            .data
11440            .rule_names()
11441            .get(request.rule_index)
11442            .map(String::as_str);
11443        let unknown_predicate_policy = self.unknown_predicate_policy;
11444        let mut ctx = ParserSemIrCtx {
11445            input: &mut self.input,
11446            tree_storage: &self.tree,
11447            semantic_hooks: &mut self.semantic_hooks,
11448            rule_index: request.rule_index,
11449            coordinate_index: request.pred_index,
11450            rule_name,
11451            context: request.context,
11452            local_int_arg: request.local_int_arg,
11453            member_values: request.member_values,
11454            invoked_predicates: &mut self.invoked_predicates,
11455            unknown_predicate_policy,
11456            unknown_predicate_hits: &mut self.unknown_predicate_hits,
11457        };
11458        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
11459    }
11460
11461    fn fast_parser_predicate_matches(
11462        &mut self,
11463        context: Option<FastPredicateContext<'_>>,
11464        transition: ParserTransition<'_>,
11465        index: usize,
11466    ) -> bool {
11467        let Some(context) = context else {
11468            return true;
11469        };
11470        let rule_index = transition.arg0() as usize;
11471        let pred_index = transition.arg1() as usize;
11472        let key = (index, rule_index, pred_index);
11473        if let Some(result) = self.fast_predicate_cache.get(&key) {
11474            return *result;
11475        }
11476        let result = self.parser_predicate_matches(PredicateEval {
11477            index,
11478            rule_index,
11479            pred_index,
11480            predicates: context.predicates,
11481            semantics: context.semantics,
11482            context: None,
11483            local_int_arg: None,
11484            member_values: context.member_values,
11485        });
11486        self.fast_predicate_cache.insert(key, result);
11487        result
11488    }
11489
11490    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
11491        let PredicateEval {
11492            index,
11493            rule_index,
11494            pred_index,
11495            predicates,
11496            semantics,
11497            context,
11498            local_int_arg,
11499            member_values,
11500        } = eval;
11501        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
11502            semantics
11503                .predicates
11504                .iter()
11505                .find(|predicate| {
11506                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
11507                })
11508                .map(|predicate| (semantics, predicate))
11509        }) {
11510            return self.parser_semir_predicate_matches(
11511                semantics,
11512                predicate,
11513                ParserSemanticHookRequest {
11514                    index,
11515                    rule_index,
11516                    pred_index,
11517                    context,
11518                    local_int_arg,
11519                    member_values,
11520                },
11521            );
11522        }
11523        let Some((_, _, predicate)) = predicates
11524            .iter()
11525            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
11526        else {
11527            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
11528                index,
11529                rule_index,
11530                pred_index,
11531                context,
11532                local_int_arg,
11533                member_values,
11534            }) {
11535                return result;
11536            }
11537            return self.unknown_predicate_result(rule_index, pred_index);
11538        };
11539        self.input.seek(index);
11540        match predicate {
11541            ParserPredicate::True => true,
11542            ParserPredicate::False => false,
11543            ParserPredicate::FalseWithMessage { .. } => false,
11544            ParserPredicate::Invoke { value } => {
11545                let key = (rule_index, pred_index);
11546                if !self.invoked_predicates.contains(&key) {
11547                    self.invoked_predicates.push(key);
11548                    use std::io::Write as _;
11549                    let mut stdout = std::io::stdout().lock();
11550                    let _ = writeln!(stdout, "eval={value}");
11551                }
11552                *value
11553            }
11554            ParserPredicate::LookaheadTextEquals { offset, text } => self
11555                .input
11556                .lt(*offset)
11557                .is_some_and(|token| Token::text(&token) == Some(*text)),
11558            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
11559                self.la(*offset) != *token_type
11560            }
11561            ParserPredicate::TokenPairAdjacent => {
11562                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
11563                    return false;
11564                };
11565                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
11566                    return false;
11567                };
11568                first + 1 == second
11569            }
11570            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
11571                .and_then(|context| {
11572                    context
11573                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
11574                        .next()
11575                        .map(crate::tree::RuleNodeView::text)
11576                })
11577                .is_none_or(|actual| actual != *text),
11578            ParserPredicate::LocalIntEquals { value } => {
11579                local_int_arg.is_none_or(|(_, actual)| actual == *value)
11580            }
11581            ParserPredicate::LocalIntLessOrEqual { value } => {
11582                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
11583            }
11584            ParserPredicate::MemberModuloEquals {
11585                member,
11586                modulus,
11587                value,
11588                equals,
11589            } => {
11590                if *modulus == 0 {
11591                    return false;
11592                }
11593                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
11594                (actual == *value) == *equals
11595            }
11596            ParserPredicate::MemberEquals {
11597                member,
11598                value,
11599                equals,
11600            } => {
11601                let actual = member_values.get(member).copied().unwrap_or_default();
11602                (actual == *value) == *equals
11603            }
11604        }
11605    }
11606
11607    /// Returns a generated fail-option message for a predicate coordinate.
11608    fn parser_predicate_failure_message(
11609        &self,
11610        rule_index: usize,
11611        pred_index: usize,
11612        predicates: &[(usize, usize, ParserPredicate)],
11613    ) -> Option<&'static str> {
11614        predicates
11615            .iter()
11616            .find_map(|(rule, pred, predicate)| match predicate {
11617                ParserPredicate::FalseWithMessage { message }
11618                    if *rule == rule_index && *pred == pred_index =>
11619                {
11620                    Some(*message)
11621                }
11622                _ => None,
11623            })
11624    }
11625
11626    /// Returns a generated fail-option message for a `SemIR` predicate
11627    /// coordinate.
11628    pub fn parser_semantic_ir_predicate_failure_message(
11629        &self,
11630        rule_index: usize,
11631        pred_index: usize,
11632        semantics: &ParserSemantics,
11633    ) -> Option<&'static str> {
11634        semantics
11635            .predicates
11636            .iter()
11637            .find(|predicate| {
11638                predicate.rule_index == rule_index && predicate.pred_index == pred_index
11639            })
11640            .and_then(|predicate| predicate.failure_message)
11641    }
11642
11643    /// Returns the token-stream index after consuming `symbol` at `index`.
11644    ///
11645    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
11646    /// index stable and rely on `consumed_eof` to record that EOF was matched.
11647    /// The parser's stream cursor is left untouched: speculative recognition
11648    /// reads ahead by absolute index, so paying for `seek` on every visited
11649    /// state would dominate the hot path. Real consumption is committed by
11650    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
11651    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
11652        if symbol == TOKEN_EOF {
11653            return index;
11654        }
11655        self.input.next_visible_after(index)
11656    }
11657
11658    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
11659    /// decision that failed after consuming a shared prefix.
11660    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
11661        let text = display_input_text(&self.input.text(start_index, error_index));
11662        diagnostic_for_token(
11663            self.token_at(error_index).as_ref(),
11664            format!("no viable alternative at input '{text}'"),
11665        )
11666    }
11667
11668    /// Selects the diagnostic for a failed consuming transition after all
11669    /// recovery repairs have been ruled out.
11670    fn recovery_failure_diagnostic(
11671        &self,
11672        index: usize,
11673        decision_start_index: Option<usize>,
11674        expected_symbols: &BTreeSet<i32>,
11675    ) -> ParserDiagnostic {
11676        if expected_symbols.len() > 1 {
11677            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11678                return self.no_viable_alternative(decision_start, index);
11679            }
11680        }
11681        diagnostic_for_token(
11682            self.token_at(index).as_ref(),
11683            format!(
11684                "mismatched input {} expecting {}",
11685                self.token_at(index)
11686                    .as_ref()
11687                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11688                self.expected_symbols_display(expected_symbols)
11689            ),
11690        )
11691    }
11692
11693    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
11694    /// instead of inserting missing tokens in the caller.
11695    fn eof_rule_recovery_diagnostic(
11696        &self,
11697        index: usize,
11698        expected_symbols: &BTreeSet<i32>,
11699        expected: &ExpectedTokens,
11700    ) -> ParserDiagnostic {
11701        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
11702            &expected.symbols
11703        } else {
11704            expected_symbols
11705        };
11706        diagnostic_for_token(
11707            self.token_at(index).as_ref(),
11708            format!(
11709                "mismatched input {} expecting {}",
11710                self.token_at(index)
11711                    .as_ref()
11712                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11713                self.expected_symbols_display(symbols)
11714            ),
11715        )
11716    }
11717
11718    /// Returns token text for a buffered token interval used by generated
11719    /// `$text` actions.
11720    ///
11721    /// ANTLR treats EOF as a range boundary rather than printable input text,
11722    /// even when an action interval explicitly stops at the EOF token.
11723    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
11724        let Some(stop) = stop else {
11725            return String::new();
11726        };
11727        let stop = if self
11728            .token_at(stop)
11729            .is_some_and(|token| token.token_type() == TOKEN_EOF)
11730        {
11731            let Some(previous) = self.previous_token_index(stop) else {
11732                return String::new();
11733            };
11734            previous
11735        } else {
11736            stop
11737        };
11738        self.input.text(start, stop)
11739    }
11740
11741    /// Resets per-parse prediction diagnostics while keeping the parser-level
11742    /// reporting flag configured by generated harness code.
11743    fn clear_prediction_diagnostics(&mut self) {
11744        self.prediction_diagnostics.clear();
11745        self.reported_prediction_diagnostics.clear();
11746    }
11747
11748    /// Drops every per-parse cache that depends on ATN identity or pins
11749    /// recovery-symbol allocations.
11750    ///
11751    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
11752    /// same parser instance can legally be driven against different grammars
11753    /// in sequence. The four caches reset here are keyed by raw ATN
11754    /// coordinates (state numbers, rule indexes) and would silently hand back
11755    /// entries from a previous ATN if reused — pruning lookahead against the
11756    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
11757    /// for the rest of the process. Clearing them on every parse entry keeps
11758    /// the perf wins (caches still amortize within one parse) without making
11759    /// long-lived parsers leak memory or surface stale ATN data:
11760    ///
11761    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
11762    ///   functions of the ATN's state graph.
11763    /// * `state_expected_cache`, `state_expected_token_cache`,
11764    ///   `rule_stop_reach_cache`, and
11765    ///   `recovery_symbols_intern` together form
11766    ///   the identity invariant that lets `FastRecognizeKey` hash
11767    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
11768    ///   so a stale interned `Rc` cannot outlive its map entry.
11769    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
11770    ///   it is retained here and invalidated lazily when the ATN changes.
11771    fn reset_per_parse_caches(&mut self) {
11772        self.rule_first_set_cache.clear();
11773        self.decision_lookahead_cache.clear();
11774        self.ll1_decision_cache.clear();
11775        self.fast_predicate_cache.clear();
11776        self.rule_stop_reach_cache.clear();
11777        self.clean_memo_mode = CleanMemoMode::Probe;
11778        self.clean_memo_probe_seen.clear();
11779        self.clean_memo_probe_samples = 0;
11780        self.clean_memo_probe_repeats = 0;
11781        self.clean_memo_sparse_samples = 0;
11782        self.recovery_symbols_intern.clear();
11783        self.state_expected_cache.clear();
11784        self.state_expected_token_cache.clear();
11785    }
11786
11787    /// Buffers ANTLR-style diagnostic-listener messages for decision states
11788    /// where multiple clean alternatives survive full-context recognition.
11789    fn record_prediction_diagnostics(
11790        &mut self,
11791        atn: &Atn,
11792        state: AtnState<'_>,
11793        start_index: usize,
11794        outcomes: &[RecognizeOutcome],
11795    ) {
11796        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
11797            return;
11798        }
11799        let Some(decision) = atn
11800            .decision_to_state()
11801            .iter()
11802            .position(|state_number| state_number == state.state_number())
11803        else {
11804            return;
11805        };
11806        let Some(rule_index) = state.rule_index() else {
11807            return;
11808        };
11809        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
11810        for outcome in outcomes
11811            .iter()
11812            .filter(|outcome| outcome.diagnostics.is_empty())
11813        {
11814            let Some(alt) = outcome.decisions.first() else {
11815                continue;
11816            };
11817            alts_by_end
11818                .entry(outcome.index)
11819                .or_default()
11820                .insert(alt + 1);
11821        }
11822        let Some((&end_index, ambig_alts)) = alts_by_end
11823            .iter()
11824            .filter(|(_, alts)| alts.len() > 1)
11825            .max_by_key(|(end, _)| *end)
11826        else {
11827            return;
11828        };
11829        let rule_name = self
11830            .rule_names()
11831            .get(rule_index)
11832            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
11833        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
11834        let input = display_input_text(&self.input.text(start_index, stop_index));
11835        let alts = ambig_alts
11836            .iter()
11837            .map(usize::to_string)
11838            .collect::<Vec<_>>()
11839            .join(", ");
11840        let key = (decision, start_index, format!("{alts}:{input}"));
11841        if !self.reported_prediction_diagnostics.insert(key) {
11842            return;
11843        }
11844        let start_diagnostic = diagnostic_for_token(
11845            self.token_at(start_index),
11846            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
11847        );
11848        let stop_diagnostic = diagnostic_for_token(
11849            self.token_at(stop_index),
11850            format!(
11851                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
11852            ),
11853        );
11854        self.prediction_diagnostics.push(start_diagnostic);
11855        self.prediction_diagnostics.push(stop_diagnostic);
11856    }
11857
11858    /// Formats the tokens expected from an ATN state using ANTLR display names.
11859    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11860        expected_symbols_display(
11861            &state_expected_symbols(atn, state_number),
11862            self.vocabulary(),
11863        )
11864    }
11865
11866    /// Expected-token set at the parser's current ATN state — ANTLR's
11867    /// `getExpectedTokens()`. Generated recognizers expose this as
11868    /// `self.expected_tokens()` for embedded test actions
11869    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
11870    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
11871        let state = usize::try_from(self.data().state()).unwrap_or(0);
11872        ExpectedTokenSet {
11873            symbols: state_expected_symbols(atn, state),
11874        }
11875    }
11876
11877    /// Enables the bail error strategy: the first syntax error aborts the
11878    /// parse instead of recovering.
11879    pub const fn set_bail_on_error(&mut self, bail: bool) {
11880        self.bail_on_error = bail;
11881    }
11882
11883    /// Whether the bail error strategy is active.
11884    #[must_use]
11885    pub const fn bail_on_error(&self) -> bool {
11886        self.bail_on_error
11887    }
11888
11889    /// Names of the rules on the live invocation stack, current rule first —
11890    /// ANTLR's `getRuleInvocationStack()`.
11891    pub fn rule_invocation_stack(&self) -> Vec<String> {
11892        self.rule_context_stack
11893            .iter()
11894            .rev()
11895            .map(|frame| {
11896                self.data()
11897                    .rule_names()
11898                    .get(frame.rule_index)
11899                    .cloned()
11900                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
11901            })
11902            .collect()
11903    }
11904
11905    /// Invoking-state chain for the active rule context, current rule first.
11906    ///
11907    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
11908    pub fn active_invocation_states(&self) -> Vec<isize> {
11909        self.rule_context_stack
11910            .iter()
11911            .skip(1)
11912            .rev()
11913            .map(|frame| frame.invoking_state)
11914            .collect()
11915    }
11916
11917    /// Formats a buffered token in ANTLR's diagnostic token display form.
11918    pub fn token_display_at(&self, index: usize) -> Option<String> {
11919        self.token_at(index).map(|token| format!("{token}"))
11920    }
11921}
11922
11923impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
11924where
11925    S: TokenSource,
11926    H: SemanticHooks,
11927{
11928    fn parse_rule(
11929        &mut self,
11930        rule_index: usize,
11931        invoking_state: isize,
11932        precedence: i32,
11933    ) -> DirectAdaptiveParseResult<ParseTree> {
11934        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
11935            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
11936        )?;
11937        let stop_state = self
11938            .atn
11939            .rule_to_stop_state()
11940            .get(rule_index)
11941            .filter(|state| *state != usize::MAX)
11942            .ok_or(DirectAdaptiveParseControl::Fallback(
11943                DirectAdaptiveFallback::MissingAtn,
11944            ))?;
11945        let start_index = self.parser.current_visible_index();
11946        let mut context = ParserRuleContext::new(rule_index, invoking_state);
11947        if let Some(token) = self.parser.token_id_at(start_index) {
11948            self.parser.set_context_start(&mut context, token);
11949        }
11950        let mut state_number = start_state;
11951        let mut consumed_eof = false;
11952        while state_number != stop_state {
11953            self.step()?;
11954            let (transition, boundary) = self.next_transition(state_number, precedence)?;
11955            if boundary.is_some() {
11956                return Err(DirectAdaptiveParseControl::Fallback(
11957                    DirectAdaptiveFallback::LeftRecursiveBoundary,
11958                ));
11959            }
11960            match transition.data() {
11961                Transition::Epsilon { target } => {
11962                    state_number = target;
11963                }
11964                Transition::Precedence {
11965                    target,
11966                    precedence: transition_precedence,
11967                } => {
11968                    if transition_precedence < precedence {
11969                        return Err(DirectAdaptiveParseControl::Fallback(
11970                            DirectAdaptiveFallback::Precedence,
11971                        ));
11972                    }
11973                    state_number = target;
11974                }
11975                Transition::Rule {
11976                    rule_index,
11977                    follow_state,
11978                    precedence: rule_precedence,
11979                    ..
11980                } => {
11981                    let child = self.parse_rule(
11982                        rule_index,
11983                        invoking_state_number(state_number),
11984                        rule_precedence,
11985                    )?;
11986                    if self.parser.build_parse_trees {
11987                        self.parser.tree.add_child(&mut context, child);
11988                    }
11989                    state_number = follow_state;
11990                }
11991                Transition::Atom { .. }
11992                | Transition::Range { .. }
11993                | Transition::Set { .. }
11994                | Transition::NotSet { .. }
11995                | Transition::Wildcard { .. } => {
11996                    let (matched_eof, child) = self.consume_transition(transition)?;
11997                    consumed_eof |= matched_eof;
11998                    if let Some(child) = child {
11999                        self.parser.tree.add_child(&mut context, child);
12000                    }
12001                    state_number = transition.target();
12002                }
12003                Transition::Predicate { .. } => {
12004                    return Err(DirectAdaptiveParseControl::Fallback(
12005                        DirectAdaptiveFallback::Predicate,
12006                    ));
12007                }
12008                Transition::Action { .. } => {
12009                    return Err(DirectAdaptiveParseControl::Fallback(
12010                        DirectAdaptiveFallback::Action,
12011                    ));
12012                }
12013            }
12014        }
12015
12016        let stop_index = self
12017            .parser
12018            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
12019        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
12020            self.parser.set_context_stop(&mut context, token);
12021        }
12022        Ok(self.parser.rule_node(context))
12023    }
12024
12025    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
12026        self.steps += 1;
12027        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
12028            return Err(DirectAdaptiveParseControl::Fallback(
12029                DirectAdaptiveFallback::StepLimit,
12030            ));
12031        }
12032        Ok(())
12033    }
12034
12035    fn next_transition(
12036        &mut self,
12037        state_number: usize,
12038        precedence: i32,
12039    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
12040        let state = self
12041            .atn
12042            .state(state_number)
12043            .ok_or(DirectAdaptiveParseControl::Fallback(
12044                DirectAdaptiveFallback::MissingAtn,
12045            ))?;
12046        if state.is_rule_stop() {
12047            return Err(DirectAdaptiveParseControl::Fallback(
12048                DirectAdaptiveFallback::RuleStop,
12049            ));
12050        }
12051        let transition_index =
12052            self.transition_index(state_number, state.transitions().len(), precedence)?;
12053        let transition = state.transitions().get(transition_index).ok_or(
12054            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
12055        )?;
12056        let boundary = match &transition.data() {
12057            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
12058                left_recursive_boundary(self.atn, state, *target)
12059            }
12060            _ => None,
12061        };
12062        Ok((transition, boundary))
12063    }
12064
12065    fn transition_index(
12066        &mut self,
12067        state_number: usize,
12068        transition_count: usize,
12069        precedence: i32,
12070    ) -> DirectAdaptiveParseResult<usize> {
12071        match transition_count {
12072            0 => Err(DirectAdaptiveParseControl::Fallback(
12073                DirectAdaptiveFallback::NoTransition,
12074            )),
12075            1 => Ok(0),
12076            _ => {
12077                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
12078                    return Ok(alt);
12079                }
12080                let decision = self
12081                    .decision_by_state
12082                    .get(state_number)
12083                    .and_then(|decision| *decision)
12084                    .ok_or(DirectAdaptiveParseControl::Fallback(
12085                        DirectAdaptiveFallback::UnknownDecision,
12086                    ))?;
12087                let prediction = self
12088                    .simulator
12089                    .adaptive_predict_stream_info_with_precedence(
12090                        decision,
12091                        direct_precedence(precedence),
12092                        &mut self.parser.input,
12093                    )
12094                    .map_err(|_| {
12095                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
12096                    })?;
12097                if prediction.has_semantic_context {
12098                    return Err(DirectAdaptiveParseControl::Fallback(
12099                        DirectAdaptiveFallback::SemanticContext,
12100                    ));
12101                }
12102                prediction
12103                    .alt
12104                    .checked_sub(1)
12105                    .filter(|index| *index < transition_count)
12106                    .ok_or(DirectAdaptiveParseControl::Fallback(
12107                        DirectAdaptiveFallback::InvalidAlt,
12108                    ))
12109            }
12110        }
12111    }
12112
12113    fn ll1_transition_index(
12114        &mut self,
12115        state_number: usize,
12116        transition_count: usize,
12117    ) -> DirectAdaptiveParseResult<Option<usize>> {
12118        let state = self
12119            .atn
12120            .state(state_number)
12121            .ok_or(DirectAdaptiveParseControl::Fallback(
12122                DirectAdaptiveFallback::MissingAtn,
12123            ))?;
12124        if state.precedence_rule_decision() {
12125            return Ok(None);
12126        }
12127        let Some(rule_stop) = state
12128            .rule_index()
12129            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
12130        else {
12131            return Ok(None);
12132        };
12133        let symbol = self.parser.input.la_token(1);
12134        let entry = self
12135            .parser
12136            .cached_decision_lookahead(self.atn, state, rule_stop);
12137        Ok(
12138            ll1_greedy_alt(&entry, symbol, state.non_greedy())
12139                .filter(|alt| *alt < transition_count),
12140        )
12141    }
12142
12143    fn consume_transition(
12144        &mut self,
12145        transition: ParserTransition<'_>,
12146    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
12147        let symbol = self.parser.input.la_token(1);
12148        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
12149            return Err(DirectAdaptiveParseControl::Fallback(
12150                DirectAdaptiveFallback::TokenMismatch,
12151            ));
12152        }
12153        let token = self
12154            .parser
12155            .input
12156            .lt_id(1)
12157            .ok_or(DirectAdaptiveParseControl::Fallback(
12158                DirectAdaptiveFallback::TokenMismatch,
12159            ))?;
12160        let matched_eof = symbol == TOKEN_EOF;
12161        if !matched_eof {
12162            self.parser.consume();
12163        }
12164        let child = self
12165            .parser
12166            .build_parse_trees
12167            .then(|| self.parser.terminal_tree(token));
12168        Ok((matched_eof, child))
12169    }
12170}
12171
12172/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
12173/// transformed left-recursive rule.
12174fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
12175    if !state.precedence_rule_decision() {
12176        return None;
12177    }
12178    let target_state = atn.state(target)?;
12179    if target_state.kind() == AtnStateKind::LoopEnd {
12180        return None;
12181    }
12182    state.rule_index()
12183}
12184
12185/// Selects the first outer alternative observed for a rule path.
12186///
12187/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
12188/// outer decision. The metadata recognizer only needs this when a generated
12189/// grammar opts into that target template; otherwise the value remains `0` and
12190/// parse-tree rendering is unchanged.
12191fn next_alt_number(
12192    state: AtnState<'_>,
12193    transition_count: usize,
12194    transition_index: usize,
12195    current_alt_number: usize,
12196    track_alt_numbers: bool,
12197) -> usize {
12198    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
12199        return current_alt_number;
12200    }
12201    if matches!(
12202        state.kind(),
12203        AtnStateKind::Basic
12204            | AtnStateKind::BlockStart
12205            | AtnStateKind::PlusBlockStart
12206            | AtnStateKind::StarBlockStart
12207            | AtnStateKind::StarLoopEntry
12208    ) && !state.precedence_rule_decision()
12209    {
12210        return transition_index + 1;
12211    }
12212    current_alt_number
12213}
12214
12215/// Converts an ATN state number into the signed invoking-state slot used by
12216/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
12217fn invoking_state_number(state_number: usize) -> isize {
12218    isize::try_from(state_number).unwrap_or(isize::MAX)
12219}
12220
12221const fn packed_i32(value: u32) -> i32 {
12222    i32::from_le_bytes(value.to_le_bytes())
12223}
12224
12225fn direct_precedence(precedence: i32) -> usize {
12226    usize::try_from(precedence.max(0)).unwrap_or_default()
12227}
12228
12229fn token_input_display(token: &impl Token) -> String {
12230    format!("'{}'", token.text().unwrap_or("<EOF>"))
12231}
12232
12233fn display_input_text(text: &str) -> String {
12234    let mut out = String::new();
12235    for ch in text.chars() {
12236        match ch {
12237            '\n' => out.push_str("\\n"),
12238            '\r' => out.push_str("\\r"),
12239            '\t' => out.push_str("\\t"),
12240            other => out.push(other),
12241        }
12242    }
12243    out
12244}
12245
12246fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
12247    let (line, column, offending) = token.map_or((0, 0, None), |token| {
12248        (token.line(), token.column(), Some(token.token_id()))
12249    });
12250    ParserDiagnostic {
12251        line,
12252        column,
12253        message,
12254        offending,
12255    }
12256}
12257
12258fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
12259    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
12260}
12261
12262fn expected_symbols_display_iter(
12263    symbols: impl IntoIterator<Item = i32>,
12264    vocabulary: &Vocabulary,
12265) -> String {
12266    let items = symbols
12267        .into_iter()
12268        .map(|symbol| expected_symbol_display(symbol, vocabulary))
12269        .collect::<Vec<_>>();
12270    if let [single] = items.as_slice() {
12271        return single.clone();
12272    }
12273    format!("{{{}}}", items.join(", "))
12274}
12275
12276fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
12277    if symbol == TOKEN_EOF {
12278        return "<EOF>".to_owned();
12279    }
12280    vocabulary.display_name(symbol)
12281}
12282
12283fn caller_follow_token_info_for_stream<S: TokenSource>(
12284    input: &mut CommonTokenStream<S>,
12285    index: usize,
12286) -> (i32, bool, bool) {
12287    // Generated callers own statement separators; leave them available when
12288    // an interpreted child rule can either stop before or consume one.
12289    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
12290        input.fill();
12291    }
12292    let token_type = input.token_type_at_index(index);
12293    let visible_channel = input.channel();
12294    let token = input.get(index);
12295    let is_boundary = token
12296        .as_ref()
12297        .and_then(Token::text)
12298        .is_some_and(is_caller_follow_boundary_text);
12299    let is_boundary_gap = token.as_ref().is_some_and(|token| {
12300        token.channel() != visible_channel
12301            || is_caller_follow_boundary_gap_text(token.text_or_empty())
12302    });
12303    (token_type, is_boundary, is_boundary_gap)
12304}
12305
12306fn is_caller_follow_boundary_text(text: &str) -> bool {
12307    text.chars().any(|ch| ch == ';' || ch == '\n')
12308        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
12309}
12310
12311fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
12312    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
12313}
12314
12315/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
12316/// Inline insertion in those precedence loops can synthesize a missing operand
12317/// before an operator and then block the legitimate loop-exit path.
12318fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
12319    let Some(rule_index) = state.rule_index() else {
12320        return false;
12321    };
12322    atn.rule_to_start_state()
12323        .get(rule_index)
12324        .and_then(|state_number| atn.state(state_number))
12325        .is_some_and(AtnState::left_recursive_rule)
12326}
12327
12328/// Picks the better of two `parse_atn_rule` passes (with and without the
12329/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
12330/// recovered one; among recovered outcomes the second pass is preferred
12331/// because the no-prefilter walk reaches ANTLR-style recovery inside child
12332/// rules. If both passes failed, the second pass's expected-token snapshot
12333/// is returned so the caller renders the same diagnostic ANTLR would.
12334fn select_better_top_outcome(
12335    first: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
12336    second: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
12337    arena: &RecognitionArena,
12338) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
12339    match (first, second) {
12340        (Ok(first), Ok(second)) => {
12341            if arena.diagnostics(first.0.diagnostics).next().is_none() {
12342                Ok(first)
12343            } else {
12344                Ok(second)
12345            }
12346        }
12347        (Ok(first), Err(_)) => Ok(first),
12348        (Err(_), Ok(second)) => Ok(second),
12349        (Err(_), Err(second_expected)) => Err(second_expected),
12350    }
12351}
12352
12353/// Chooses the outermost parse result that consumed the most input.
12354///
12355/// The recognizer intentionally keeps shorter endpoints available while walking
12356/// nested rule transitions so callers can satisfy following tokens such as
12357/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
12358fn select_best_fast_outcome(
12359    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
12360    prediction_mode: PredictionMode,
12361    caller_follow: Option<&TokenBitSet>,
12362    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
12363    arena: &RecognitionArena,
12364) -> Option<FastRecognizeOutcome> {
12365    let mut best = None;
12366    let mut best_caller_follow = None;
12367    for outcome in outcomes {
12368        if matches!(
12369            prediction_mode,
12370            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
12371        ) && outcome.diagnostics.is_empty()
12372            && let Some(follow) = caller_follow
12373        {
12374            let (token_type, is_boundary, _) = token_info_at(outcome.index);
12375            if is_boundary && follow.contains(token_type) {
12376                let replace =
12377                    best_caller_follow
12378                        .as_ref()
12379                        .is_none_or(|existing: &FastRecognizeOutcome| {
12380                            (outcome.index, outcome.consumed_eof)
12381                                < (existing.index, existing.consumed_eof)
12382                        });
12383                if replace {
12384                    best_caller_follow = Some(outcome);
12385                }
12386            }
12387        }
12388        let Some(existing) = best else {
12389            best = Some(outcome);
12390            continue;
12391        };
12392        let outcome_position = (outcome.index, outcome.consumed_eof);
12393        let best_position = (existing.index, existing.consumed_eof);
12394        let better = match prediction_mode {
12395            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
12396                outcome_position,
12397                outcome.diagnostics,
12398                best_position,
12399                existing.diagnostics,
12400                arena,
12401            ),
12402            PredictionMode::Sll => outcome.index > existing.index,
12403        };
12404        best = Some(if better { outcome } else { existing });
12405    }
12406    let should_use_caller_follow =
12407        best_caller_follow
12408            .as_ref()
12409            .zip(best.as_ref())
12410            .is_some_and(|(candidate, selected)| {
12411                if !selected.diagnostics.is_empty() {
12412                    return true;
12413                }
12414                candidate.index < selected.index
12415                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
12416            });
12417    if should_use_caller_follow {
12418        best_caller_follow
12419    } else {
12420        best
12421    }
12422}
12423
12424fn select_best_outcome(
12425    outcomes: impl Iterator<Item = RecognizeOutcome>,
12426    prediction_mode: PredictionMode,
12427    arena: &RecognitionArena,
12428) -> Option<RecognizeOutcome> {
12429    let outcomes = outcomes.collect::<Vec<_>>();
12430    let prefer_first_tie = outcomes
12431        .iter()
12432        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
12433    outcomes.into_iter().reduce(|best, outcome| {
12434        let outcome_position = (outcome.index, outcome.consumed_eof);
12435        let best_position = (best.index, best.consumed_eof);
12436        let better = match prediction_mode {
12437            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
12438                outcome_is_better(
12439                    outcome_position,
12440                    outcome.diagnostics,
12441                    best_position,
12442                    best.diagnostics,
12443                    arena,
12444                ) || (outcome_position == best_position
12445                    && arena.diagnostics_len(outcome.diagnostics)
12446                        == arena.diagnostics_len(best.diagnostics)
12447                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
12448                        == arena.diagnostics_recovery_rank(best.diagnostics)
12449                    && (outcome.decisions < best.decisions
12450                        || (!prefer_first_tie
12451                            && outcome.decisions == best.decisions
12452                            && outcome.actions > best.actions)))
12453            }
12454            PredictionMode::Sll => {
12455                outcome_position > best_position
12456                    || (outcome_position == best_position
12457                        && !prefer_first_tie
12458                        && (outcome.decisions < best.decisions
12459                            || (outcome.decisions == best.decisions
12460                                && outcome_is_better(
12461                                    outcome_position,
12462                                    outcome.diagnostics,
12463                                    best_position,
12464                                    best.diagnostics,
12465                                    arena,
12466                                ))))
12467            }
12468        };
12469        if better {
12470            return outcome;
12471        }
12472        best
12473    })
12474}
12475
12476/// Records the serialized transition order at parser decision states.
12477///
12478/// When two clean paths consume the same input, ANTLR's adaptive prediction
12479/// chooses by alternative order. Keeping this compact trace lets the metadata
12480/// recognizer distinguish greedy and non-greedy optional blocks without a full
12481/// prediction simulator.
12482fn transition_decision(
12483    atn: &Atn,
12484    state: AtnState<'_>,
12485    transition_count: usize,
12486    transition_index: usize,
12487    predicates: &[(usize, usize, ParserPredicate)],
12488) -> Option<usize> {
12489    if transition_count <= 1 || decision_reaches_unsupported_predicate(atn, state, predicates) {
12490        return None;
12491    }
12492    Some(transition_index)
12493}
12494
12495/// Reports whether a state should reset the active no-viable decision start.
12496///
12497/// Loop entry/back states are continuations of the surrounding adaptive
12498/// prediction; resetting at those states would turn LL-star failures back into
12499/// ordinary mismatches.
12500fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
12501    transition_count > 1
12502        && !matches!(
12503            state.kind(),
12504            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
12505        )
12506}
12507
12508/// Marks a farthest expected-token set as no-viable when multiple alternatives
12509/// failed after the active decision had already consumed input.
12510fn record_no_viable_if_ambiguous(
12511    expected: &mut ExpectedTokens,
12512    decision_start_index: Option<usize>,
12513    index: usize,
12514) {
12515    if expected.index == Some(index) && expected.symbols.len() > 1 {
12516        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
12517            expected.record_no_viable(decision_start, index);
12518        }
12519    }
12520}
12521
12522/// Records a no-viable decision caused by a failed semantic predicate before
12523/// any consuming transition can contribute an expected-token set.
12524const fn record_predicate_no_viable(
12525    expected: &mut ExpectedTokens,
12526    decision_start_index: Option<usize>,
12527    index: usize,
12528) {
12529    if let Some(decision_start) = decision_start_index {
12530        expected.record_no_viable(decision_start, index);
12531    }
12532}
12533
12534/// Returns the active decision start only when the error is past that start.
12535const fn no_viable_decision_start(
12536    decision_start_index: Option<usize>,
12537    index: usize,
12538) -> Option<usize> {
12539    match decision_start_index {
12540        Some(start) if index > start => Some(start),
12541        _ => None,
12542    }
12543}
12544
12545/// Restores expected-token bookkeeping when a child rule found a clean
12546/// consuming path; failures in longer child alternatives should not pollute the
12547/// caller's final expectation set.
12548fn restore_expected(
12549    children: &[RecognizeOutcome],
12550    child_start_index: usize,
12551    expected: &mut ExpectedTokens,
12552    snapshot: ExpectedTokens,
12553    preserve_child_expected: bool,
12554) {
12555    if preserve_child_expected {
12556        return;
12557    }
12558    if children
12559        .iter()
12560        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
12561    {
12562        *expected = snapshot;
12563    }
12564}
12565
12566/// Reports whether a decision can reach a predicate the generator did not
12567/// translate. Static alternative order is unsafe for those context predicates.
12568fn decision_reaches_unsupported_predicate(
12569    atn: &Atn,
12570    state: AtnState<'_>,
12571    predicates: &[(usize, usize, ParserPredicate)],
12572) -> bool {
12573    state.transitions().iter().any(|transition| {
12574        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
12575    })
12576}
12577
12578/// Walks epsilon-like edges from one transition to find unsupported predicates.
12579fn transition_reaches_unsupported_predicate(
12580    atn: &Atn,
12581    transition: ParserTransition<'_>,
12582    predicates: &[(usize, usize, ParserPredicate)],
12583    visited: &mut BTreeSet<usize>,
12584) -> bool {
12585    match &transition.data() {
12586        Transition::Predicate {
12587            rule_index,
12588            pred_index,
12589            ..
12590        } => !predicates
12591            .iter()
12592            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
12593        Transition::Epsilon { target }
12594        | Transition::Action { target, .. }
12595        | Transition::Rule { target, .. } => {
12596            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
12597        }
12598        Transition::Precedence { .. }
12599        | Transition::Atom { .. }
12600        | Transition::Range { .. }
12601        | Transition::Set { .. }
12602        | Transition::NotSet { .. }
12603        | Transition::Wildcard { .. } => false,
12604    }
12605}
12606
12607/// Finds an unsupported predicate reachable before a consuming transition.
12608fn state_reaches_unsupported_predicate(
12609    atn: &Atn,
12610    state_number: usize,
12611    predicates: &[(usize, usize, ParserPredicate)],
12612    visited: &mut BTreeSet<usize>,
12613) -> bool {
12614    if !visited.insert(state_number) {
12615        return false;
12616    }
12617    let Some(state) = atn.state(state_number) else {
12618        return false;
12619    };
12620    state.transitions().iter().any(|transition| {
12621        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
12622    })
12623}
12624
12625/// Adds a decision step to the front of an already-recognized suffix path.
12626fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
12627    if let Some(decision) = decision {
12628        outcome.decisions.insert(0, decision);
12629    }
12630}
12631
12632fn outcome_is_better(
12633    outcome_position: (usize, bool),
12634    outcome_diagnostics: DiagnosticSeqId,
12635    best_position: (usize, bool),
12636    best_diagnostics: DiagnosticSeqId,
12637    arena: &RecognitionArena,
12638) -> bool {
12639    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
12640    let best_len = arena.diagnostics_len(best_diagnostics);
12641    outcome_position > best_position
12642        || (outcome_position == best_position
12643            && (outcome_len < best_len
12644                || (outcome_len == best_len
12645                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
12646                        < arena.diagnostics_recovery_rank(best_diagnostics))))
12647}
12648
12649fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
12650    if outcomes
12651        .iter()
12652        .any(|outcome| outcome.diagnostics.is_empty())
12653    {
12654        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12655    }
12656}
12657
12658fn discard_recovered_outcomes_if_clean_path_exists(
12659    outcomes: &mut Vec<RecognizeOutcome>,
12660    arena: &RecognitionArena,
12661) {
12662    if outcomes
12663        .iter()
12664        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
12665    {
12666        return;
12667    }
12668    if outcomes
12669        .iter()
12670        .any(|outcome| outcome.diagnostics.is_empty())
12671    {
12672        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12673    }
12674}
12675
12676/// Reports whether a recovered outcome came from an explicit predicate
12677/// fail-option and therefore should compete with shorter clean loop exits.
12678fn outcome_has_rule_failure_diagnostic(
12679    outcome: &RecognizeOutcome,
12680    arena: &RecognitionArena,
12681) -> bool {
12682    arena
12683        .diagnostics(outcome.diagnostics)
12684        .any(|diagnostic| diagnostic.message.starts_with("rule "))
12685}
12686
12687/// Removes equivalent endpoints before memoizing a state result while
12688/// preserving ATN transition-discovery order.
12689///
12690/// Outcomes are compared on observable recognition state — the input index,
12691/// EOF consumption, and diagnostics — without descending into the parse-tree
12692/// fragment carried by `nodes`. Two paths reaching the same point with
12693/// different node trees would otherwise prevent memoization from collapsing
12694/// equivalent suffixes and explode the speculative-path cache.
12695///
12696/// The first occurrence per recognition key wins, which matches ANTLR's
12697/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
12698/// transitions before loop-exit, so the first-discovered outcome carries the
12699/// greedy parse-tree fragment.
12700fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
12701    if outcomes.len() < 2 {
12702        return;
12703    }
12704    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
12705    outcomes.retain(|outcome| {
12706        seen.insert((
12707            outcome.index,
12708            outcome.consumed_eof,
12709            arena.diagnostics_len(outcome.diagnostics),
12710            arena.diagnostics_recovery_rank(outcome.diagnostics),
12711        ))
12712    });
12713}
12714
12715const FAST_OUTCOME_INLINE_KEYS: usize = 8;
12716const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
12717const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
12718const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
12719
12720#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12721enum FastOutcomeDedupStrategy {
12722    Inline,
12723    Dense,
12724    Sparse,
12725}
12726
12727impl FastOutcomeDedupScratch {
12728    fn prepare_dense(&mut self, word_count: usize) {
12729        while let Some(word_index) = self.touched_dense_words.pop() {
12730            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
12731        }
12732        if self.dense_words.len() < word_count {
12733            self.dense_words.resize(word_count, 0);
12734        }
12735    }
12736}
12737
12738fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
12739    let first_index = outcomes.first()?.index;
12740    let (min_index, max_index) = outcomes[1..].iter().fold(
12741        (first_index, first_index),
12742        |(min_index, max_index), outcome| {
12743            (min_index.min(outcome.index), max_index.max(outcome.index))
12744        },
12745    );
12746    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
12747    let bit_count = index_span.checked_mul(2)?;
12748    let word_count =
12749        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
12750    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
12751    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
12752    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
12753        .then_some((min_index, word_count))
12754}
12755
12756#[cfg(feature = "perf-counters")]
12757fn record_clean_fast_outcome_dedup(
12758    strategy: FastOutcomeDedupStrategy,
12759    input_len: usize,
12760    output_len: usize,
12761    dense_words: usize,
12762) {
12763    let counter = match strategy {
12764        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
12765        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
12766        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
12767    };
12768    perf_counters::inc(
12769        &perf_counters::OUTCOME_DEDUPE_INPUTS,
12770        u64::try_from(input_len).unwrap_or(u64::MAX),
12771    );
12772    perf_counters::inc(
12773        &perf_counters::OUTCOME_DEDUPE_REMOVED,
12774        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
12775    );
12776    perf_counters::inc(counter, 1);
12777    perf_counters::inc(
12778        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
12779        u64::try_from(dense_words).unwrap_or(u64::MAX),
12780    );
12781}
12782
12783/// Removes duplicate clean endpoints while preserving transition-discovery
12784/// order. Tiny lists stay on the stack; larger compact ranges use a direct
12785/// bitmap, and only wide sparse ranges pay for hashing.
12786fn dedupe_clean_fast_outcomes(
12787    outcomes: &mut Vec<FastRecognizeOutcome>,
12788    scratch: &mut FastOutcomeDedupScratch,
12789) -> FastOutcomeDedupStrategy {
12790    #[cfg(feature = "perf-counters")]
12791    let input_len = outcomes.len();
12792    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
12793        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
12794        let mut inline_len = 0_usize;
12795        outcomes.retain(|outcome| {
12796            let key = (outcome.index, outcome.consumed_eof);
12797            if inline_keys[..inline_len].contains(&key) {
12798                return false;
12799            }
12800            inline_keys[inline_len] = key;
12801            inline_len += 1;
12802            true
12803        });
12804        #[cfg(feature = "perf-counters")]
12805        record_clean_fast_outcome_dedup(
12806            FastOutcomeDedupStrategy::Inline,
12807            input_len,
12808            outcomes.len(),
12809            0,
12810        );
12811        return FastOutcomeDedupStrategy::Inline;
12812    }
12813
12814    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
12815        scratch.prepare_dense(word_count);
12816        outcomes.retain(|outcome| {
12817            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
12818            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
12819            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
12820            let word = &mut scratch.dense_words[word_index];
12821            if *word & bit != 0 {
12822                return false;
12823            }
12824            if *word == 0 {
12825                scratch
12826                    .touched_dense_words
12827                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
12828            }
12829            *word |= bit;
12830            true
12831        });
12832        #[cfg(feature = "perf-counters")]
12833        record_clean_fast_outcome_dedup(
12834            FastOutcomeDedupStrategy::Dense,
12835            input_len,
12836            outcomes.len(),
12837            word_count,
12838        );
12839        return FastOutcomeDedupStrategy::Dense;
12840    }
12841
12842    scratch.sparse_keys.clear();
12843    scratch.sparse_keys.reserve(outcomes.len());
12844    outcomes.retain(|outcome| {
12845        scratch
12846            .sparse_keys
12847            .insert((outcome.index, outcome.consumed_eof))
12848    });
12849    #[cfg(feature = "perf-counters")]
12850    record_clean_fast_outcome_dedup(
12851        FastOutcomeDedupStrategy::Sparse,
12852        input_len,
12853        outcomes.len(),
12854        0,
12855    );
12856    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
12857        scratch.sparse_keys = FxHashSet::default();
12858    }
12859    FastOutcomeDedupStrategy::Sparse
12860}
12861
12862/// Sorts and removes equivalent endpoints, including action traces and the
12863/// arena-backed node sequence's structural contents.
12864fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
12865    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
12866    outcomes
12867        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
12868}
12869
12870fn compare_recognize_outcomes(
12871    left: &RecognizeOutcome,
12872    right: &RecognizeOutcome,
12873    arena: &RecognitionArena,
12874) -> Ordering {
12875    left.index
12876        .cmp(&right.index)
12877        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
12878        .then_with(|| left.alt_number.cmp(&right.alt_number))
12879        .then_with(|| left.member_values.cmp(&right.member_values))
12880        .then_with(|| left.return_values.cmp(&right.return_values))
12881        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
12882        .then_with(|| left.decisions.cmp(&right.decisions))
12883        .then_with(|| left.actions.cmp(&right.actions))
12884        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
12885}
12886
12887impl<S, H> Recognizer for BaseParser<S, H>
12888where
12889    S: TokenSource,
12890    H: SemanticHooks,
12891{
12892    fn data(&self) -> &RecognizerData {
12893        &self.data
12894    }
12895
12896    fn data_mut(&mut self) -> &mut RecognizerData {
12897        &mut self.data
12898    }
12899}
12900
12901impl<S, H> Parser for BaseParser<S, H>
12902where
12903    S: TokenSource,
12904    H: SemanticHooks,
12905{
12906    fn build_parse_trees(&self) -> bool {
12907        self.build_parse_trees
12908    }
12909
12910    fn set_build_parse_trees(&mut self, build: bool) {
12911        self.build_parse_trees = build;
12912    }
12913
12914    fn number_of_syntax_errors(&self) -> usize {
12915        Self::number_of_syntax_errors(self)
12916    }
12917
12918    fn report_diagnostic_errors(&self) -> bool {
12919        self.report_diagnostic_errors
12920    }
12921
12922    fn set_report_diagnostic_errors(&mut self, report: bool) {
12923        self.report_diagnostic_errors = report;
12924    }
12925
12926    fn prediction_mode(&self) -> PredictionMode {
12927        self.prediction_mode
12928    }
12929
12930    fn set_prediction_mode(&mut self, mode: PredictionMode) {
12931        self.prediction_mode = mode;
12932    }
12933
12934    fn max_rule_depth(&self) -> Option<usize> {
12935        self.max_rule_depth
12936    }
12937
12938    fn set_max_rule_depth(&mut self, depth: Option<usize>) {
12939        self.max_rule_depth = depth;
12940    }
12941
12942    fn add_parse_listener(&mut self, listener: Box<dyn ParseListener>) {
12943        self.parse_listeners.push(ParseListenerSlot(listener));
12944    }
12945
12946    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
12947        Self::remove_parse_listeners(self)
12948    }
12949}
12950
12951#[cfg(test)]
12952#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
12953mod tests {
12954    use super::*;
12955    use crate::atn::parser::{
12956        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
12957    };
12958    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
12959    use crate::token::{
12960        HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError, TokenView,
12961    };
12962    use crate::token_stream::CommonTokenStream;
12963    use crate::tree::{NodeKind, ParseTreeStats};
12964    use crate::vocabulary::Vocabulary;
12965    use std::cell::RefCell;
12966    use std::mem::size_of;
12967    use std::rc::Rc;
12968    use std::sync::{Arc, Mutex};
12969
12970    #[test]
12971    fn fx_hasher_write_matches_typed_methods_for_full_words() {
12972        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
12973        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
12974        // fields) must hash the same as the typed methods, otherwise an
12975        // `FxHashMap` keyed on such a type silently disagrees with itself
12976        // depending on which entry point the caller used. Verify the
12977        // little-endian word equivalence this PR established.
12978        let value: u64 = 0x0102_0304_0506_0708;
12979        let mut typed = FxHasher::default();
12980        typed.write_u64(value);
12981        let mut bytewise = FxHasher::default();
12982        bytewise.write(&value.to_le_bytes());
12983        assert_eq!(typed.finish(), bytewise.finish());
12984    }
12985
12986    #[derive(Clone, Debug)]
12987    struct TestToken {
12988        spec: TokenSpec,
12989        id: TokenId,
12990        source_name: String,
12991    }
12992
12993    impl TestToken {
12994        fn new(token_type: i32) -> Self {
12995            Self {
12996                spec: TokenSpec::explicit(token_type, ""),
12997                id: TokenId::try_from(0).expect("zero token ID"),
12998                source_name: String::new(),
12999            }
13000        }
13001
13002        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
13003            Self {
13004                spec: TokenSpec::eof(index, index, line, column),
13005                id: TokenId::try_from(0).expect("zero token ID"),
13006                source_name: source_name.to_owned(),
13007            }
13008        }
13009
13010        fn with_text(mut self, text: impl Into<String>) -> Self {
13011            self.spec.text = Some(text.into());
13012            self
13013        }
13014
13015        const fn with_channel(mut self, channel: i32) -> Self {
13016            self.spec.channel = channel;
13017            self
13018        }
13019
13020        const fn with_span(mut self, start: usize, stop: usize) -> Self {
13021            self.spec.start = start;
13022            self.spec.stop = stop;
13023            self.spec.start_byte = start;
13024            self.spec.stop_byte = match stop.checked_add(1) {
13025                Some(end) if end >= start => end,
13026                Some(_) | None => start,
13027            };
13028            self
13029        }
13030
13031        const fn with_position(mut self, line: usize, column: usize) -> Self {
13032            self.spec.line = line;
13033            self.spec.column = column;
13034            self
13035        }
13036
13037        fn set_token_index(&mut self, index: isize) {
13038            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
13039        }
13040    }
13041
13042    impl Token for TestToken {
13043        fn token_id(&self) -> TokenId {
13044            self.id
13045        }
13046
13047        fn token_type(&self) -> i32 {
13048            self.spec.token_type
13049        }
13050
13051        fn channel(&self) -> i32 {
13052            self.spec.channel
13053        }
13054
13055        fn start(&self) -> usize {
13056            self.spec.start
13057        }
13058
13059        fn stop(&self) -> usize {
13060            self.spec.stop
13061        }
13062
13063        fn line(&self) -> usize {
13064            self.spec.line
13065        }
13066
13067        fn column(&self) -> usize {
13068            self.spec.column
13069        }
13070
13071        fn text(&self) -> Option<&str> {
13072            self.spec.text.as_deref()
13073        }
13074
13075        fn source_name(&self) -> &str {
13076            &self.source_name
13077        }
13078
13079        fn start_byte(&self) -> usize {
13080            self.spec.start_byte
13081        }
13082
13083        fn stop_byte(&self) -> usize {
13084            self.spec.stop_byte
13085        }
13086    }
13087
13088    #[derive(Debug)]
13089    struct Source {
13090        tokens: Vec<TestToken>,
13091        index: usize,
13092    }
13093
13094    impl TokenSource for Source {
13095        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
13096            let token = self
13097                .tokens
13098                .get(self.index)
13099                .cloned()
13100                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
13101            self.index += 1;
13102            sink.push(token.spec)
13103        }
13104
13105        fn line(&self) -> usize {
13106            1
13107        }
13108
13109        fn column(&self) -> usize {
13110            self.index
13111        }
13112
13113        fn source_name(&self) -> &'static str {
13114            "parser-test"
13115        }
13116    }
13117
13118    #[derive(Clone, Debug, Eq, PartialEq)]
13119    struct RecordedDiagnostic {
13120        grammar_file_name: String,
13121        offending_text: Option<String>,
13122        line: usize,
13123        column: usize,
13124        message: String,
13125        error: Option<AntlrError>,
13126    }
13127
13128    #[derive(Clone, Debug)]
13129    struct RecordingErrorListener {
13130        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
13131    }
13132
13133    impl<R> crate::ErrorListener<R> for RecordingErrorListener
13134    where
13135        R: Recognizer + ?Sized,
13136    {
13137        fn syntax_error(
13138            &mut self,
13139            recognizer: &R,
13140            offending: Option<TokenView<'_>>,
13141            line: usize,
13142            column: usize,
13143            message: &str,
13144            error: Option<&AntlrError>,
13145        ) {
13146            self.diagnostics
13147                .lock()
13148                .expect("recorded diagnostics lock")
13149                .push(RecordedDiagnostic {
13150                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
13151                    offending_text: offending.and_then(|token| token.text().map(str::to_owned)),
13152                    line,
13153                    column,
13154                    message: message.to_owned(),
13155                    error: error.cloned(),
13156                });
13157        }
13158    }
13159
13160    #[derive(Debug)]
13161    struct ReportingSource {
13162        source: Source,
13163        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
13164    }
13165
13166    impl TokenSource for ReportingSource {
13167        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
13168            self.source.next_token(sink)
13169        }
13170
13171        fn line(&self) -> usize {
13172            self.source.line()
13173        }
13174
13175        fn column(&self) -> usize {
13176            self.source.column()
13177        }
13178
13179        fn source_name(&self) -> &str {
13180            self.source.source_name()
13181        }
13182
13183        fn report_error(&self, error: &TokenSourceError) -> bool {
13184            self.diagnostics.borrow_mut().push(error.clone());
13185            true
13186        }
13187    }
13188
13189    fn mini_parser_data() -> RecognizerData {
13190        RecognizerData::new(
13191            "Mini.g4",
13192            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
13193        )
13194        .with_rule_names(["s"])
13195    }
13196
13197    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
13198        let data = mini_parser_data();
13199        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
13200    }
13201
13202    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
13203    where
13204        H: SemanticHooks,
13205    {
13206        BaseParser::with_semantic_hooks(
13207            CommonTokenStream::new(Source { tokens, index: 0 }),
13208            mini_parser_data(),
13209            hooks,
13210        )
13211    }
13212
13213    #[test]
13214    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
13215        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
13216        parser.remove_error_listeners();
13217        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13218        parser.add_error_listener(RecordingErrorListener {
13219            diagnostics: Arc::clone(&diagnostics),
13220        });
13221        let parser_diagnostics = [ParserDiagnostic {
13222            line: 1,
13223            column: 2,
13224            message: "missing 'x' at 'y'".to_owned(),
13225            offending: None,
13226        }];
13227        let token_errors = [
13228            TokenSourceError::new(1, 1, "token recognition error at: '@'"),
13229            TokenSourceError::new(1, 3, "token recognition error at: '#'"),
13230        ];
13231
13232        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
13233
13234        // The interleaved token/parser diagnostic stream (ordering, columns, messages) is one
13235        // reviewable snapshot instead of three hand-written RecordedDiagnostic literals.
13236        insta::assert_debug_snapshot!(
13237            "parser_dispatches_recovery_diagnostics_through_registered_listeners",
13238            *diagnostics.lock().expect("recorded diagnostics lock")
13239        );
13240
13241        parser.remove_error_listeners();
13242        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
13243        assert_eq!(
13244            diagnostics.lock().expect("recorded diagnostics lock").len(),
13245            3
13246        );
13247    }
13248
13249    #[test]
13250    fn recovery_diagnostics_expose_the_offending_token_to_listeners() {
13251        let mut parser = mini_parser(vec![
13252            TestToken::new(7)
13253                .with_text("oops")
13254                .with_span(0, 3)
13255                .with_position(1, 2),
13256            TestToken::eof("parser-test", 4, 1, 6),
13257        ]);
13258        parser.remove_error_listeners();
13259        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13260        parser.add_error_listener(RecordingErrorListener {
13261            diagnostics: Arc::clone(&diagnostics),
13262        });
13263        let offending = parser.input.lt_id(1);
13264        assert!(offending.is_some(), "current token should be buffered");
13265        let parser_diagnostics = [ParserDiagnostic {
13266            line: 1,
13267            column: 2,
13268            message: "extraneous input 'oops'".to_owned(),
13269            offending,
13270        }];
13271
13272        parser.dispatch_generated_diagnostics(&parser_diagnostics, &[]);
13273
13274        // Listeners receive a resolvable view of the offending token — the
13275        // ANTLR offendingSymbol contract downstream span-building error
13276        // reporters (miette-style byte-offset underlines) rely on.
13277        let recorded = diagnostics
13278            .lock()
13279            .expect("recorded diagnostics lock")
13280            .clone();
13281        insta::assert_debug_snapshot!(
13282            "recovery_diagnostics_expose_the_offending_token_to_listeners",
13283            recorded
13284        );
13285    }
13286
13287    #[test]
13288    fn parser_leaves_token_errors_to_source_owned_listeners() {
13289        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
13290        let source = ReportingSource {
13291            source: Source {
13292                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
13293                index: 0,
13294            },
13295            diagnostics: Rc::clone(&source_diagnostics),
13296        };
13297        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
13298        parser.remove_error_listeners();
13299        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
13300        parser.add_error_listener(RecordingErrorListener {
13301            diagnostics: Arc::clone(&parser_diagnostics),
13302        });
13303        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
13304
13305        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
13306
13307        assert_eq!(*source_diagnostics.borrow(), [source_error]);
13308        assert!(
13309            parser_diagnostics
13310                .lock()
13311                .expect("recorded diagnostics lock")
13312                .is_empty()
13313        );
13314    }
13315
13316    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
13317        builder.finish().expect("valid packed parser ATN")
13318    }
13319
13320    fn nested_rule_chain_atn(depth: usize) -> Atn {
13321        nested_rule_graph_atn(depth, false, false)
13322    }
13323
13324    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
13325        assert!(depth > 0);
13326        let mut atn = ParserAtnBuilder::new(2);
13327        let mut starts = Vec::with_capacity(depth);
13328        let mut stops = Vec::with_capacity(depth);
13329        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
13330        for rule_index in 0..depth {
13331            starts.push(
13332                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
13333                    .expect("rule start")
13334                    .index(),
13335            );
13336        }
13337        for rule_index in 0..depth {
13338            stops.push(
13339                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
13340                    .expect("rule stop")
13341                    .index(),
13342            );
13343        }
13344        if consuming_follows {
13345            for rule_index in 0..depth - 1 {
13346                follows.push(
13347                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
13348                        .expect("rule follow")
13349                        .index(),
13350                );
13351            }
13352        }
13353        atn.set_rule_to_start_state(starts.clone())
13354            .expect("rule start states");
13355        atn.set_rule_to_stop_state(stops.clone())
13356            .expect("rule stop states");
13357        for rule_index in 0..depth - 1 {
13358            let follow_state = if consuming_follows {
13359                follows[rule_index]
13360            } else {
13361                stops[rule_index]
13362            };
13363            atn.add_transition(
13364                starts[rule_index],
13365                ParserTransitionSpec::Rule {
13366                    target: starts[rule_index + 1],
13367                    rule_index: rule_index + 1,
13368                    follow_state,
13369                    precedence: 0,
13370                },
13371            )
13372            .expect("nested rule transition");
13373            if branching {
13374                atn.add_transition(
13375                    starts[rule_index],
13376                    ParserTransitionSpec::Atom {
13377                        target: stops[rule_index],
13378                        label: 2,
13379                    },
13380                )
13381                .expect("dead branch transition");
13382            }
13383            if consuming_follows {
13384                atn.add_transition(
13385                    follow_state,
13386                    ParserTransitionSpec::Atom {
13387                        target: stops[rule_index],
13388                        label: 1,
13389                    },
13390                )
13391                .expect("consuming follow transition");
13392            }
13393        }
13394        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
13395        atn.add_transition(
13396            starts[depth - 1],
13397            ParserTransitionSpec::Set {
13398                target: stops[depth - 1],
13399                set: token_set,
13400            },
13401        )
13402        .expect("terminal set transition");
13403        if branching {
13404            atn.add_transition(
13405                starts[depth - 1],
13406                ParserTransitionSpec::Atom {
13407                    target: stops[depth - 1],
13408                    label: 2,
13409                },
13410            )
13411            .expect("dead leaf branch transition");
13412        }
13413        finish_atn(atn)
13414    }
13415
13416    fn ordinary_star_loop_atn() -> Atn {
13417        let mut atn = ParserAtnBuilder::new(2);
13418        for (state_number, kind, rule_index) in [
13419            (0, AtnStateKind::RuleStart, 0),
13420            (1, AtnStateKind::StarLoopEntry, 0),
13421            (2, AtnStateKind::Basic, 0),
13422            (3, AtnStateKind::StarLoopBack, 0),
13423            (4, AtnStateKind::LoopEnd, 0),
13424            (5, AtnStateKind::Basic, 0),
13425            (6, AtnStateKind::RuleStop, 0),
13426            (7, AtnStateKind::RuleStart, 1),
13427            (8, AtnStateKind::Basic, 1),
13428            (9, AtnStateKind::RuleStop, 1),
13429        ] {
13430            assert_eq!(
13431                atn.add_state(kind, Some(rule_index))
13432                    .expect("state")
13433                    .index(),
13434                state_number
13435            );
13436        }
13437        atn.set_rule_to_start_state(vec![0, 7])
13438            .expect("rule start states");
13439        atn.set_rule_to_stop_state(vec![6, 9])
13440            .expect("rule stop states");
13441        atn.add_decision_state(1).expect("decision state");
13442        atn.set_loop_back_state(4, 3).expect("loop back state");
13443        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13444            .expect("transition");
13445        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13446            .expect("transition");
13447        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
13448            .expect("transition");
13449        atn.add_transition(
13450            2,
13451            ParserTransitionSpec::Rule {
13452                target: 7,
13453                rule_index: 1,
13454                follow_state: 3,
13455                precedence: 0,
13456            },
13457        )
13458        .expect("transition");
13459        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
13460            .expect("transition");
13461        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13462            .expect("transition");
13463        atn.add_transition(
13464            5,
13465            ParserTransitionSpec::Atom {
13466                target: 6,
13467                label: TOKEN_EOF,
13468            },
13469        )
13470        .expect("transition");
13471        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13472            .expect("transition");
13473        atn.add_transition(
13474            8,
13475            ParserTransitionSpec::Atom {
13476                target: 9,
13477                label: 1,
13478            },
13479        )
13480        .expect("transition");
13481        finish_atn(atn)
13482    }
13483
13484    /// ATN for `s : (X | X X)* EOF`.
13485    fn ambiguous_ordinary_star_loop_atn() -> Atn {
13486        let mut atn = ParserAtnBuilder::new(1);
13487        for (state_number, kind) in [
13488            (0, AtnStateKind::RuleStart),
13489            (1, AtnStateKind::StarLoopEntry),
13490            (2, AtnStateKind::StarBlockStart),
13491            (3, AtnStateKind::Basic),
13492            (4, AtnStateKind::BlockEnd),
13493            (5, AtnStateKind::StarLoopBack),
13494            (6, AtnStateKind::LoopEnd),
13495            (7, AtnStateKind::Basic),
13496            (8, AtnStateKind::RuleStop),
13497        ] {
13498            assert_eq!(
13499                atn.add_state(kind, Some(0)).expect("state").index(),
13500                state_number
13501            );
13502        }
13503        atn.set_rule_to_start_state(vec![0])
13504            .expect("rule start states");
13505        atn.set_rule_to_stop_state(vec![8])
13506            .expect("rule stop states");
13507        atn.set_end_state(2, 4).expect("block end state");
13508        atn.set_loop_back_state(6, 5).expect("loop back state");
13509        atn.add_decision_state(1).expect("decision state");
13510        atn.add_decision_state(2).expect("decision state");
13511        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13512            .expect("transition");
13513        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13514            .expect("transition");
13515        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
13516            .expect("transition");
13517        atn.add_transition(
13518            2,
13519            ParserTransitionSpec::Atom {
13520                target: 4,
13521                label: 1,
13522            },
13523        )
13524        .expect("transition");
13525        atn.add_transition(
13526            2,
13527            ParserTransitionSpec::Atom {
13528                target: 3,
13529                label: 1,
13530            },
13531        )
13532        .expect("transition");
13533        atn.add_transition(
13534            3,
13535            ParserTransitionSpec::Atom {
13536                target: 4,
13537                label: 1,
13538            },
13539        )
13540        .expect("transition");
13541        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13542            .expect("transition");
13543        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
13544            .expect("transition");
13545        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13546            .expect("transition");
13547        atn.add_transition(
13548            7,
13549            ParserTransitionSpec::Atom {
13550                target: 8,
13551                label: TOKEN_EOF,
13552            },
13553        )
13554        .expect("transition");
13555        finish_atn(atn)
13556    }
13557
13558    fn ordinary_plus_loop_atn() -> Atn {
13559        let mut atn = ParserAtnBuilder::new(2);
13560        for (state_number, kind, rule_index) in [
13561            (0, AtnStateKind::RuleStart, 0),
13562            (1, AtnStateKind::Basic, 0),
13563            (2, AtnStateKind::PlusLoopBack, 0),
13564            (3, AtnStateKind::LoopEnd, 0),
13565            (4, AtnStateKind::Basic, 0),
13566            (5, AtnStateKind::RuleStop, 0),
13567            (6, AtnStateKind::RuleStart, 1),
13568            (7, AtnStateKind::Basic, 1),
13569            (8, AtnStateKind::RuleStop, 1),
13570        ] {
13571            assert_eq!(
13572                atn.add_state(kind, Some(rule_index))
13573                    .expect("state")
13574                    .index(),
13575                state_number
13576            );
13577        }
13578        atn.set_rule_to_start_state(vec![0, 6])
13579            .expect("rule start states");
13580        atn.set_rule_to_stop_state(vec![5, 8])
13581            .expect("rule stop states");
13582        atn.add_decision_state(2).expect("decision state");
13583        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13584            .expect("transition");
13585        atn.add_transition(
13586            1,
13587            ParserTransitionSpec::Rule {
13588                target: 6,
13589                rule_index: 1,
13590                follow_state: 2,
13591                precedence: 0,
13592            },
13593        )
13594        .expect("transition");
13595        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
13596            .expect("transition");
13597        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13598            .expect("transition");
13599        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13600            .expect("transition");
13601        atn.add_transition(
13602            4,
13603            ParserTransitionSpec::Atom {
13604                target: 5,
13605                label: TOKEN_EOF,
13606            },
13607        )
13608        .expect("transition");
13609        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13610            .expect("transition");
13611        atn.add_transition(
13612            7,
13613            ParserTransitionSpec::Atom {
13614                target: 8,
13615                label: 1,
13616            },
13617        )
13618        .expect("transition");
13619        finish_atn(atn)
13620    }
13621
13622    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
13623        let mut tokens = (0..count)
13624            .map(|_| TestToken::new(1).with_text("x"))
13625            .collect::<Vec<_>>();
13626        tokens.push(TestToken::eof("parser-test", count, 1, count));
13627        tokens
13628    }
13629
13630    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
13631        let mut atn = ParserAtnBuilder::new(2);
13632        assert_eq!(
13633            atn.add_state(AtnStateKind::RuleStart, Some(0))
13634                .expect("state")
13635                .index(),
13636            0
13637        );
13638        assert_eq!(
13639            atn.add_state(AtnStateKind::Basic, Some(0))
13640                .expect("state")
13641                .index(),
13642            1
13643        );
13644        assert_eq!(
13645            atn.add_state(AtnStateKind::Basic, Some(0))
13646                .expect("state")
13647                .index(),
13648            2
13649        );
13650        assert_eq!(
13651            atn.add_state(AtnStateKind::RuleStart, Some(1))
13652                .expect("state")
13653                .index(),
13654            3
13655        );
13656        atn.set_left_recursive_rule(3)
13657            .expect("left-recursive rule start");
13658        assert_eq!(
13659            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
13660                .expect("state")
13661                .index(),
13662            4
13663        );
13664        atn.set_precedence_rule_decision(4)
13665            .expect("precedence decision");
13666        assert_eq!(
13667            atn.add_state(AtnStateKind::Basic, Some(1))
13668                .expect("state")
13669                .index(),
13670            5
13671        );
13672        assert_eq!(
13673            atn.add_state(AtnStateKind::Basic, Some(1))
13674                .expect("state")
13675                .index(),
13676            6
13677        );
13678        assert_eq!(
13679            atn.add_state(AtnStateKind::LoopEnd, Some(1))
13680                .expect("state")
13681                .index(),
13682            7
13683        );
13684        assert_eq!(
13685            atn.add_state(AtnStateKind::RuleStop, Some(1))
13686                .expect("state")
13687                .index(),
13688            8
13689        );
13690        assert_eq!(
13691            atn.add_state(AtnStateKind::RuleStop, Some(0))
13692                .expect("state")
13693                .index(),
13694            9
13695        );
13696        atn.set_rule_to_start_state(vec![0, 3])
13697            .expect("rule start states");
13698        atn.set_rule_to_stop_state(vec![9, 8])
13699            .expect("rule stop states");
13700        atn.add_transition(
13701            1,
13702            ParserTransitionSpec::Rule {
13703                target: 3,
13704                rule_index: 1,
13705                follow_state: 2,
13706                precedence: 0,
13707            },
13708        )
13709        .expect("transition");
13710        atn.add_transition(
13711            2,
13712            ParserTransitionSpec::Atom {
13713                target: 9,
13714                label: caller_symbol,
13715            },
13716        )
13717        .expect("transition");
13718        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13719            .expect("transition");
13720        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
13721            .expect("transition");
13722        atn.add_transition(
13723            5,
13724            ParserTransitionSpec::Precedence {
13725                target: 6,
13726                precedence: 1,
13727            },
13728        )
13729        .expect("transition");
13730        atn.add_transition(
13731            6,
13732            ParserTransitionSpec::Atom {
13733                target: 4,
13734                label: 1,
13735            },
13736        )
13737        .expect("transition");
13738        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13739            .expect("transition");
13740        finish_atn(atn)
13741    }
13742
13743    fn labeled_left_recursive_operator_atn() -> Atn {
13744        let mut atn = ParserAtnBuilder::new(4);
13745        for (state, kind) in [
13746            (0, AtnStateKind::RuleStart),
13747            (1, AtnStateKind::BlockStart),
13748            (2, AtnStateKind::StarLoopEntry),
13749            (3, AtnStateKind::StarBlockStart),
13750            (4, AtnStateKind::Basic),
13751            (5, AtnStateKind::Basic),
13752            (6, AtnStateKind::Basic),
13753            (7, AtnStateKind::StarLoopBack),
13754            (8, AtnStateKind::LoopEnd),
13755            (9, AtnStateKind::RuleStop),
13756        ] {
13757            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13758        }
13759        atn.set_left_recursive_rule(0)
13760            .expect("left-recursive rule start");
13761        atn.set_precedence_rule_decision(2)
13762            .expect("precedence decision");
13763        atn.set_loop_back_state(8, 7).expect("loop-back state");
13764        atn.set_rule_to_start_state(vec![0])
13765            .expect("rule start states");
13766        atn.set_rule_to_stop_state(vec![9])
13767            .expect("rule stop states");
13768        for state in [1, 2, 3] {
13769            atn.add_decision_state(state).expect("decision state");
13770        }
13771        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
13772            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
13773                .expect("epsilon transition");
13774        }
13775        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
13776            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
13777                .expect("token transition");
13778        }
13779        for (target, precedence) in [(4, 2), (5, 1)] {
13780            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
13781                .expect("operator precedence");
13782        }
13783        finish_atn(atn)
13784    }
13785
13786    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
13787        let mut parser = mini_parser(vec![
13788            TestToken::new(symbol).with_text("lookahead"),
13789            TestToken::eof("parser-test", 1, 1, 1),
13790        ]);
13791        parser.rule_context_stack = vec![
13792            RuleContextFrame {
13793                rule_index: 0,
13794                invoking_state: -1,
13795            },
13796            RuleContextFrame {
13797                rule_index: 1,
13798                invoking_state: 1,
13799            },
13800        ];
13801        parser
13802    }
13803
13804    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
13805        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
13806        //   prec 2: token 1, token 1  (shift `>>`)
13807        //   prec 1: token 1           (relational `>`)
13808        let mut atn = ParserAtnBuilder::new(1);
13809        for (state, kind, rule) in [
13810            (0, AtnStateKind::RuleStart, 0),
13811            (1, AtnStateKind::StarLoopEntry, 0),
13812            (2, AtnStateKind::Basic, 0), // ops hub
13813            (3, AtnStateKind::Basic, 0), // shift prec
13814            (4, AtnStateKind::Basic, 0), // shift first >
13815            (5, AtnStateKind::Basic, 0), // shift second >
13816            (6, AtnStateKind::Basic, 0), // rel prec
13817            (7, AtnStateKind::Basic, 0), // rel >
13818            (8, AtnStateKind::LoopEnd, 0),
13819            (9, AtnStateKind::RuleStop, 0),
13820        ] {
13821            assert_eq!(
13822                atn.add_state(kind, Some(rule)).expect("state").index(),
13823                state
13824            );
13825            if state == 0 {
13826                atn.set_left_recursive_rule(state)
13827                    .expect("left-recursive rule start");
13828            } else if state == 1 {
13829                atn.set_precedence_rule_decision(state)
13830                    .expect("precedence decision");
13831            }
13832        }
13833        atn.set_rule_to_start_state(vec![0])
13834            .expect("rule start states");
13835        atn.set_rule_to_stop_state(vec![9])
13836            .expect("rule stop states");
13837        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13838            .expect("ops");
13839        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13840            .expect("exit");
13841        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13842            .expect("to shift");
13843        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13844            .expect("to rel");
13845        atn.add_transition(
13846            3,
13847            ParserTransitionSpec::Precedence {
13848                target: 4,
13849                precedence: 2,
13850            },
13851        )
13852        .expect("shift prec");
13853        atn.add_transition(
13854            4,
13855            ParserTransitionSpec::Atom {
13856                target: 5,
13857                label: 1,
13858            },
13859        )
13860        .expect("shift first >");
13861        atn.add_transition(
13862            5,
13863            ParserTransitionSpec::Atom {
13864                target: 1,
13865                label: 1,
13866            },
13867        )
13868        .expect("shift second >");
13869        atn.add_transition(
13870            6,
13871            ParserTransitionSpec::Precedence {
13872                target: 7,
13873                precedence: 1,
13874            },
13875        )
13876        .expect("rel prec");
13877        atn.add_transition(
13878            7,
13879            ParserTransitionSpec::Atom {
13880                target: 1,
13881                label: 1,
13882            },
13883        )
13884        .expect("rel >");
13885        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13886            .expect("loop end");
13887        finish_atn(atn)
13888    }
13889
13890    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
13891        let mut atn = ParserAtnBuilder::new(2);
13892        for (state, kind, rule) in [
13893            (0, AtnStateKind::RuleStart, 0),
13894            (1, AtnStateKind::StarLoopEntry, 0),
13895            (2, AtnStateKind::Basic, 0),
13896            (3, AtnStateKind::Basic, 0),
13897            (4, AtnStateKind::Basic, 0),
13898            (5, AtnStateKind::Basic, 0),
13899            (6, AtnStateKind::Basic, 0),
13900            (7, AtnStateKind::Basic, 0),
13901            (8, AtnStateKind::LoopEnd, 0),
13902            (9, AtnStateKind::RuleStop, 0),
13903            (10, AtnStateKind::RuleStart, 1),
13904            (11, AtnStateKind::Basic, 1),
13905            (12, AtnStateKind::RuleStop, 1),
13906        ] {
13907            assert_eq!(
13908                atn.add_state(kind, Some(rule)).expect("state").index(),
13909                state
13910            );
13911            if state == 0 {
13912                atn.set_left_recursive_rule(state)
13913                    .expect("left-recursive rule start");
13914            } else if state == 1 {
13915                atn.set_precedence_rule_decision(state)
13916                    .expect("precedence decision");
13917            }
13918        }
13919        atn.set_rule_to_start_state(vec![0, 10])
13920            .expect("rule start states");
13921        atn.set_rule_to_stop_state(vec![9, 12])
13922            .expect("rule stop states");
13923        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13924            .expect("ops");
13925        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13926            .expect("exit");
13927        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13928            .expect("to shift");
13929        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13930            .expect("to relational");
13931        atn.add_transition(
13932            3,
13933            ParserTransitionSpec::Precedence {
13934                target: 4,
13935                precedence: 2,
13936            },
13937        )
13938        .expect("shift precedence");
13939        atn.add_transition(
13940            4,
13941            ParserTransitionSpec::Rule {
13942                target: 10,
13943                rule_index: 1,
13944                follow_state: 5,
13945                precedence: 0,
13946            },
13947        )
13948        .expect("first shift token helper");
13949        atn.add_transition(
13950            5,
13951            ParserTransitionSpec::Atom {
13952                target: 1,
13953                label: 1,
13954            },
13955        )
13956        .expect("second shift token");
13957        atn.add_transition(
13958            6,
13959            ParserTransitionSpec::Precedence {
13960                target: 7,
13961                precedence: 1,
13962            },
13963        )
13964        .expect("relational precedence");
13965        atn.add_transition(
13966            7,
13967            ParserTransitionSpec::Atom {
13968                target: 1,
13969                label: 1,
13970            },
13971        )
13972        .expect("relational token");
13973        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
13974            .expect("loop end");
13975        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
13976            .expect("helper entry");
13977        atn.add_transition(
13978            11,
13979            ParserTransitionSpec::Atom {
13980                target: 12,
13981                label: 1,
13982            },
13983        )
13984        .expect("first shift token");
13985        finish_atn(atn)
13986    }
13987
13988    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
13989        let mut atn = ParserAtnBuilder::new(1);
13990        for (state, kind) in [
13991            (0, AtnStateKind::RuleStart),
13992            (1, AtnStateKind::StarLoopEntry),
13993            (2, AtnStateKind::Basic),
13994            (3, AtnStateKind::Basic),
13995            (4, AtnStateKind::Basic),
13996            (5, AtnStateKind::Basic),
13997            (6, AtnStateKind::Basic),
13998            (7, AtnStateKind::Basic),
13999            (8, AtnStateKind::Basic),
14000            (9, AtnStateKind::LoopEnd),
14001            (10, AtnStateKind::RuleStop),
14002        ] {
14003            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
14004            if state == 0 {
14005                atn.set_left_recursive_rule(state)
14006                    .expect("left-recursive rule start");
14007            } else if state == 1 {
14008                atn.set_precedence_rule_decision(state)
14009                    .expect("precedence decision");
14010            }
14011        }
14012        atn.set_rule_to_start_state(vec![0])
14013            .expect("rule start states");
14014        atn.set_rule_to_stop_state(vec![10])
14015            .expect("rule stop states");
14016        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14017            .expect("ops");
14018        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
14019            .expect("exit");
14020        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
14021            .expect("to multi-token operator");
14022        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
14023            .expect("to predicate operator");
14024        atn.add_transition(
14025            3,
14026            ParserTransitionSpec::Precedence {
14027                target: 4,
14028                precedence: 2,
14029            },
14030        )
14031        .expect("multi-token precedence");
14032        atn.add_transition(
14033            4,
14034            ParserTransitionSpec::Atom {
14035                target: 5,
14036                label: 1,
14037            },
14038        )
14039        .expect("multi-token first");
14040        atn.add_transition(
14041            5,
14042            ParserTransitionSpec::Atom {
14043                target: 1,
14044                label: 1,
14045            },
14046        )
14047        .expect("multi-token second");
14048        atn.add_transition(
14049            6,
14050            ParserTransitionSpec::Precedence {
14051                target: 7,
14052                precedence: 2,
14053            },
14054        )
14055        .expect("predicate precedence");
14056        atn.add_transition(
14057            7,
14058            ParserTransitionSpec::Predicate {
14059                target: 8,
14060                rule_index: 0,
14061                pred_index: 0,
14062                context_dependent: false,
14063            },
14064        )
14065        .expect("operator predicate");
14066        atn.add_transition(
14067            8,
14068            ParserTransitionSpec::Atom {
14069                target: 1,
14070                label: 1,
14071            },
14072        )
14073        .expect("predicate single token");
14074        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14075            .expect("loop end");
14076        finish_atn(atn)
14077    }
14078
14079    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
14080        let mut atn = ParserAtnBuilder::new(2);
14081        for (state, kind, rule) in [
14082            (0, AtnStateKind::RuleStart, 0),
14083            (1, AtnStateKind::StarLoopEntry, 0),
14084            (2, AtnStateKind::Basic, 0),
14085            (3, AtnStateKind::Basic, 0),
14086            (4, AtnStateKind::Basic, 0),
14087            (5, AtnStateKind::LoopEnd, 0),
14088            (6, AtnStateKind::RuleStop, 0),
14089            (7, AtnStateKind::RuleStart, 1),
14090            (8, AtnStateKind::RuleStop, 1),
14091            (9, AtnStateKind::Basic, 1),
14092        ] {
14093            assert_eq!(
14094                atn.add_state(kind, Some(rule)).expect("state").index(),
14095                state
14096            );
14097            if state == 0 {
14098                atn.set_left_recursive_rule(state)
14099                    .expect("left-recursive rule start");
14100            } else if state == 1 {
14101                atn.set_precedence_rule_decision(state)
14102                    .expect("precedence decision");
14103            }
14104        }
14105        atn.set_rule_to_start_state(vec![0, 7])
14106            .expect("rule start states");
14107        atn.set_rule_to_stop_state(vec![6, 8])
14108            .expect("rule stop states");
14109        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14110            .expect("transition");
14111        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
14112            .expect("transition");
14113        atn.add_transition(
14114            2,
14115            ParserTransitionSpec::Precedence {
14116                target: 3,
14117                precedence: 3,
14118            },
14119        )
14120        .expect("transition");
14121        atn.add_transition(
14122            3,
14123            ParserTransitionSpec::Rule {
14124                target: 7,
14125                rule_index: 1,
14126                follow_state: 4,
14127                precedence: 0,
14128            },
14129        )
14130        .expect("transition");
14131        atn.add_transition(
14132            4,
14133            ParserTransitionSpec::Atom {
14134                target: 1,
14135                label: 1,
14136            },
14137        )
14138        .expect("transition");
14139        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14140            .expect("transition");
14141        atn.add_transition(
14142            7,
14143            ParserTransitionSpec::Precedence {
14144                target: 9,
14145                precedence: 1,
14146            },
14147        )
14148        .expect("transition");
14149        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
14150            .expect("transition");
14151        finish_atn(atn)
14152    }
14153
14154    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
14155        let mut atn = ParserAtnBuilder::new(2);
14156        for (state, kind) in [
14157            (0, AtnStateKind::RuleStart),
14158            (1, AtnStateKind::StarLoopEntry),
14159            (2, AtnStateKind::Basic),
14160            (3, AtnStateKind::Basic),
14161            (4, AtnStateKind::Basic),
14162            (5, AtnStateKind::LoopEnd),
14163            (6, AtnStateKind::RuleStop),
14164        ] {
14165            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
14166            if state == 0 {
14167                atn.set_left_recursive_rule(state)
14168                    .expect("left-recursive rule start");
14169            } else if state == 1 {
14170                atn.set_precedence_rule_decision(state)
14171                    .expect("precedence decision");
14172            }
14173        }
14174        atn.set_rule_to_start_state(vec![0])
14175            .expect("rule start states");
14176        atn.set_rule_to_stop_state(vec![6])
14177            .expect("rule stop states");
14178        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14179            .expect("transition");
14180        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
14181            .expect("transition");
14182        atn.add_transition(
14183            2,
14184            ParserTransitionSpec::Precedence {
14185                target: 3,
14186                precedence: 1,
14187            },
14188        )
14189        .expect("transition");
14190        atn.add_transition(
14191            3,
14192            ParserTransitionSpec::Predicate {
14193                target: 4,
14194                rule_index: 0,
14195                pred_index: 0,
14196                context_dependent: false,
14197            },
14198        )
14199        .expect("transition");
14200        atn.add_transition(
14201            4,
14202            ParserTransitionSpec::Atom {
14203                target: 1,
14204                label: 1,
14205            },
14206        )
14207        .expect("transition");
14208        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14209            .expect("transition");
14210        finish_atn(atn)
14211    }
14212
14213    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
14214        let mut atn = ParserAtnBuilder::new(2);
14215        for (state, kind, rule) in [
14216            (0, AtnStateKind::RuleStart, 0),
14217            (1, AtnStateKind::Basic, 0),
14218            (2, AtnStateKind::Basic, 0),
14219            (3, AtnStateKind::Basic, 0),
14220            (4, AtnStateKind::RuleStop, 0),
14221            (5, AtnStateKind::RuleStart, 1),
14222            (6, AtnStateKind::StarLoopEntry, 1),
14223            (7, AtnStateKind::Basic, 1),
14224            (8, AtnStateKind::Basic, 1),
14225            (9, AtnStateKind::LoopEnd, 1),
14226            (10, AtnStateKind::RuleStop, 1),
14227            (11, AtnStateKind::RuleStart, 2),
14228            (12, AtnStateKind::RuleStop, 2),
14229        ] {
14230            assert_eq!(
14231                atn.add_state(kind, Some(rule)).expect("state").index(),
14232                state
14233            );
14234            if state == 5 {
14235                atn.set_left_recursive_rule(state)
14236                    .expect("left-recursive rule start");
14237            } else if state == 6 {
14238                atn.set_precedence_rule_decision(state)
14239                    .expect("precedence decision");
14240            }
14241        }
14242        atn.set_rule_to_start_state(vec![0, 5, 11])
14243            .expect("rule start states");
14244        atn.set_rule_to_stop_state(vec![4, 10, 12])
14245            .expect("rule stop states");
14246        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14247            .expect("transition");
14248        atn.add_transition(
14249            1,
14250            ParserTransitionSpec::Rule {
14251                target: 5,
14252                rule_index: 1,
14253                follow_state: 2,
14254                precedence: 0,
14255            },
14256        )
14257        .expect("transition");
14258        atn.add_transition(
14259            2,
14260            ParserTransitionSpec::Rule {
14261                target: 11,
14262                rule_index: 2,
14263                follow_state: 3,
14264                precedence: 0,
14265            },
14266        )
14267        .expect("transition");
14268        atn.add_transition(
14269            3,
14270            ParserTransitionSpec::Atom {
14271                target: 4,
14272                label: caller_symbol,
14273            },
14274        )
14275        .expect("transition");
14276        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
14277            .expect("transition");
14278        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
14279            .expect("transition");
14280        atn.add_transition(
14281            7,
14282            ParserTransitionSpec::Precedence {
14283                target: 8,
14284                precedence: 1,
14285            },
14286        )
14287        .expect("transition");
14288        atn.add_transition(
14289            8,
14290            ParserTransitionSpec::Atom {
14291                target: 6,
14292                label: 1,
14293            },
14294        )
14295        .expect("transition");
14296        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14297            .expect("transition");
14298        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
14299            .expect("transition");
14300        finish_atn(atn)
14301    }
14302
14303    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
14304        let mut atn = ParserAtnBuilder::new(2);
14305        for (state, kind, rule) in [
14306            (0, AtnStateKind::RuleStart, 0),
14307            (1, AtnStateKind::Basic, 0),
14308            (2, AtnStateKind::Basic, 0),
14309            (3, AtnStateKind::RuleStop, 0),
14310            (4, AtnStateKind::RuleStart, 1),
14311            (5, AtnStateKind::Basic, 1),
14312            (6, AtnStateKind::Basic, 1),
14313            (7, AtnStateKind::RuleStop, 1),
14314            (8, AtnStateKind::RuleStart, 2),
14315            (9, AtnStateKind::StarLoopEntry, 2),
14316            (10, AtnStateKind::Basic, 2),
14317            (11, AtnStateKind::Basic, 2),
14318            (12, AtnStateKind::LoopEnd, 2),
14319            (13, AtnStateKind::RuleStop, 2),
14320        ] {
14321            assert_eq!(
14322                atn.add_state(kind, Some(rule)).expect("state").index(),
14323                state
14324            );
14325            if state == 8 {
14326                atn.set_left_recursive_rule(state)
14327                    .expect("left-recursive rule start");
14328            } else if state == 9 {
14329                atn.set_precedence_rule_decision(state)
14330                    .expect("precedence decision");
14331            }
14332        }
14333        atn.set_rule_to_start_state(vec![0, 4, 8])
14334            .expect("rule start states");
14335        atn.set_rule_to_stop_state(vec![3, 7, 13])
14336            .expect("rule stop states");
14337        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14338            .expect("transition");
14339        atn.add_transition(
14340            1,
14341            ParserTransitionSpec::Rule {
14342                target: 4,
14343                rule_index: 1,
14344                follow_state: 2,
14345                precedence: 0,
14346            },
14347        )
14348        .expect("transition");
14349        atn.add_transition(
14350            2,
14351            ParserTransitionSpec::Atom {
14352                target: 3,
14353                label: caller_symbol,
14354            },
14355        )
14356        .expect("transition");
14357        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14358            .expect("transition");
14359        atn.add_transition(
14360            5,
14361            ParserTransitionSpec::Rule {
14362                target: 8,
14363                rule_index: 2,
14364                follow_state: 6,
14365                precedence: 0,
14366            },
14367        )
14368        .expect("transition");
14369        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
14370            .expect("transition");
14371        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14372            .expect("transition");
14373        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
14374            .expect("transition");
14375        atn.add_transition(
14376            10,
14377            ParserTransitionSpec::Precedence {
14378                target: 11,
14379                precedence: 1,
14380            },
14381        )
14382        .expect("transition");
14383        atn.add_transition(
14384            11,
14385            ParserTransitionSpec::Atom {
14386                target: 9,
14387                label: 1,
14388            },
14389        )
14390        .expect("transition");
14391        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
14392            .expect("transition");
14393        finish_atn(atn)
14394    }
14395
14396    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
14397        let mut atn = ParserAtnBuilder::new(2);
14398        for (state, kind, rule) in [
14399            (0, AtnStateKind::RuleStart, 0),
14400            (1, AtnStateKind::Basic, 0),
14401            (2, AtnStateKind::Basic, 0),
14402            (3, AtnStateKind::RuleStop, 0),
14403            (4, AtnStateKind::RuleStart, 1),
14404            (5, AtnStateKind::StarLoopEntry, 1),
14405            (6, AtnStateKind::Basic, 1),
14406            (7, AtnStateKind::Basic, 1),
14407            (8, AtnStateKind::Basic, 1),
14408            (9, AtnStateKind::Basic, 1),
14409            (10, AtnStateKind::LoopEnd, 1),
14410            (11, AtnStateKind::RuleStop, 1),
14411        ] {
14412            assert_eq!(
14413                atn.add_state(kind, Some(rule)).expect("state").index(),
14414                state
14415            );
14416            if state == 4 {
14417                atn.set_left_recursive_rule(state)
14418                    .expect("left-recursive rule start");
14419            } else if state == 5 {
14420                atn.set_precedence_rule_decision(state)
14421                    .expect("precedence decision");
14422            }
14423        }
14424        atn.set_rule_to_start_state(vec![0, 4])
14425            .expect("rule start states");
14426        atn.set_rule_to_stop_state(vec![3, 11])
14427            .expect("rule stop states");
14428        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14429            .expect("transition");
14430        atn.add_transition(
14431            1,
14432            ParserTransitionSpec::Rule {
14433                target: 4,
14434                rule_index: 1,
14435                follow_state: 2,
14436                precedence: 0,
14437            },
14438        )
14439        .expect("transition");
14440        atn.add_transition(
14441            2,
14442            ParserTransitionSpec::Atom {
14443                target: 3,
14444                label: caller_symbol,
14445            },
14446        )
14447        .expect("transition");
14448        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14449            .expect("transition");
14450        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
14451            .expect("transition");
14452        atn.add_transition(
14453            6,
14454            ParserTransitionSpec::Precedence {
14455                target: 7,
14456                precedence: 1,
14457            },
14458        )
14459        .expect("transition");
14460        atn.add_transition(
14461            7,
14462            ParserTransitionSpec::Atom {
14463                target: 8,
14464                label: 1,
14465            },
14466        )
14467        .expect("transition");
14468        atn.add_transition(
14469            8,
14470            ParserTransitionSpec::Rule {
14471                target: 4,
14472                rule_index: 1,
14473                follow_state: 9,
14474                precedence: 2,
14475            },
14476        )
14477        .expect("transition");
14478        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
14479            .expect("transition");
14480        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
14481            .expect("transition");
14482        finish_atn(atn)
14483    }
14484
14485    #[test]
14486    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
14487        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
14488        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
14489
14490        let mut overlapping = parser_inside_left_recursive_callee(1);
14491        assert_eq!(
14492            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
14493            None
14494        );
14495
14496        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
14497        assert_eq!(
14498            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14499            Some(true)
14500        );
14501
14502        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
14503        assert_eq!(
14504            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14505            Some(false)
14506        );
14507
14508        assert_eq!(
14509            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14510            Some(true),
14511            "overlap results must not leak across ATNs"
14512        );
14513    }
14514
14515    #[test]
14516    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
14517        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
14518        let mut parser = mini_parser(vec![
14519            TestToken::new(1).with_text("operator"),
14520            TestToken::eof("parser-test", 1, 1, 1),
14521        ]);
14522        parser.rule_context_stack = vec![RuleContextFrame {
14523            rule_index: 0,
14524            invoking_state: -1,
14525        }];
14526
14527        assert_eq!(
14528            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14529            Some(true)
14530        );
14531        assert_eq!(
14532            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14533            Some(true),
14534            "cached operator lookahead must preserve the nullable prefix return path"
14535        );
14536        assert_eq!(
14537            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14538            Some(true),
14539            "the nullable child must use its rule-call precedence, not the caller precedence"
14540        );
14541    }
14542
14543    #[test]
14544    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
14545        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
14546        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
14547        // must defer so StarLoopEntry adaptive predict can exit when the second
14548        // `>` is absent (as in `a < b > c`).
14549        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
14550        let mut parser = mini_parser(vec![
14551            TestToken::new(1).with_text(">"),
14552            TestToken::new(2).with_text("id"),
14553            TestToken::eof("parser-test", 1, 1, 1),
14554        ]);
14555        parser.rule_context_stack = vec![RuleContextFrame {
14556            rule_index: 0,
14557            invoking_state: -1,
14558        }];
14559
14560        assert_eq!(
14561            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14562            Some(true),
14563            "at low precedence relational `>` is a single-token operator"
14564        );
14565        assert_eq!(
14566            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
14567            Some(true),
14568            "relational remains single-token at its own precedence"
14569        );
14570        assert_eq!(
14571            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14572            None,
14573            "at shift precedence, bare `>` must not force enter"
14574        );
14575    }
14576
14577    #[test]
14578    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
14579        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
14580        let mut parser = mini_parser(vec![
14581            TestToken::new(1).with_text(">"),
14582            TestToken::new(2).with_text("id"),
14583            TestToken::eof("parser-test", 1, 1, 1),
14584        ]);
14585        parser.rule_context_stack = vec![RuleContextFrame {
14586            rule_index: 0,
14587            invoking_state: -1,
14588        }];
14589
14590        assert_eq!(
14591            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14592            Some(true),
14593            "the direct relational alternative remains a one-token operator"
14594        );
14595        assert_eq!(
14596            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14597            None,
14598            "a token matched in the helper rule must return to the second shift token"
14599        );
14600    }
14601
14602    #[test]
14603    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
14604        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
14605        let mut parser = mini_parser(vec![
14606            TestToken::new(1).with_text(">"),
14607            TestToken::new(2).with_text("id"),
14608            TestToken::eof("parser-test", 1, 1, 1),
14609        ]);
14610        parser.rule_context_stack = vec![RuleContextFrame {
14611            rule_index: 0,
14612            invoking_state: -1,
14613        }];
14614
14615        assert_eq!(
14616            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14617            None,
14618            "a predicate-gated single-token path must not be hidden by a multi-token path"
14619        );
14620    }
14621
14622    #[test]
14623    fn left_recursive_loop_defers_predicate_guarded_operator() {
14624        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
14625        let mut parser = mini_parser_with_hooks(
14626            vec![
14627                TestToken::new(1).with_text("operator"),
14628                TestToken::eof("parser-test", 1, 1, 1),
14629            ],
14630            RejectingPredicateHooks::default(),
14631        );
14632        parser.rule_context_stack = vec![RuleContextFrame {
14633            rule_index: 0,
14634            invoking_state: -1,
14635        }];
14636
14637        assert_eq!(
14638            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14639            None,
14640            "a false predicate must be evaluated before entering the operator alternative"
14641        );
14642        assert_eq!(
14643            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14644            None,
14645            "cached predicate-dependent lookahead must keep deferring"
14646        );
14647    }
14648
14649    #[test]
14650    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
14651        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
14652        let mut parser = parser_inside_left_recursive_callee(1);
14653
14654        assert_eq!(
14655            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14656            None
14657        );
14658        assert_eq!(
14659            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14660            None,
14661            "the cached overlap must preserve the nullable child return path"
14662        );
14663    }
14664
14665    #[test]
14666    fn left_recursive_loop_defers_through_nullable_parent_return() {
14667        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
14668        let mut parser = mini_parser(vec![
14669            TestToken::new(1).with_text("lookahead"),
14670            TestToken::eof("parser-test", 1, 1, 1),
14671        ]);
14672        parser.rule_context_stack = vec![
14673            RuleContextFrame {
14674                rule_index: 0,
14675                invoking_state: -1,
14676            },
14677            RuleContextFrame {
14678                rule_index: 1,
14679                invoking_state: 1,
14680            },
14681            RuleContextFrame {
14682                rule_index: 2,
14683                invoking_state: 5,
14684            },
14685        ];
14686
14687        assert_eq!(
14688            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14689            None,
14690            "a nullable caller must unwind to its parent's consuming follow path"
14691        );
14692        assert_eq!(
14693            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14694            None,
14695            "the caller-overlap cache must not retain a false negative"
14696        );
14697    }
14698
14699    #[test]
14700    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
14701        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
14702        let mut parser = mini_parser(vec![
14703            TestToken::new(1).with_text("lookahead"),
14704            TestToken::eof("parser-test", 1, 1, 1),
14705        ]);
14706        parser.rule_context_stack = vec![
14707            RuleContextFrame {
14708                rule_index: 0,
14709                invoking_state: -1,
14710            },
14711            RuleContextFrame {
14712                rule_index: 1,
14713                invoking_state: 1,
14714            },
14715            RuleContextFrame {
14716                rule_index: 1,
14717                invoking_state: 8,
14718            },
14719        ];
14720
14721        assert_eq!(
14722            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14723            None,
14724            "a recursive operand return must preserve its parent caller context"
14725        );
14726        assert_eq!(
14727            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14728            None,
14729            "the caller-overlap cache must preserve the loop-boundary return"
14730        );
14731    }
14732
14733    fn token_then_eof_atn() -> Atn {
14734        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14735            4, 1, 2, // version, parser, max token type
14736            3, // states
14737            2, 0, // rule start
14738            1, 0, // basic
14739            7, 0, // rule stop
14740            0, // non-greedy states
14741            0, // precedence states
14742            1, // rules
14743            0, // rule 0 start
14744            0, // modes
14745            0, // sets
14746            2, // transitions
14747            0, 1, 5, 1, 0, 0, // match token 1
14748            1, 2, 5, -1, 0, 0, // match EOF
14749            0, // decisions
14750        ]))
14751        .deserialize_parser()
14752        .expect("artificial parser ATN should deserialize")
14753    }
14754
14755    fn epsilon_cycle_atn() -> Atn {
14756        let mut atn = ParserAtnBuilder::new(1);
14757        for (state_number, kind) in [
14758            (0, AtnStateKind::RuleStart),
14759            (1, AtnStateKind::Basic),
14760            (2, AtnStateKind::RuleStop),
14761        ] {
14762            assert_eq!(
14763                atn.add_state(kind, Some(0)).expect("state").index(),
14764                state_number
14765            );
14766        }
14767        atn.set_rule_to_start_state(vec![0])
14768            .expect("rule start states");
14769        atn.set_rule_to_stop_state(vec![2])
14770            .expect("rule stop states");
14771        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14772            .expect("transition");
14773        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
14774            .expect("self-cycle transition");
14775        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14776            .expect("exit transition");
14777        finish_atn(atn)
14778    }
14779
14780    fn eof_then_action_atn() -> Atn {
14781        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14782            4, 1, 1, // version, parser, max token type
14783            3, // states
14784            2, 0, // rule start
14785            1, 0, // basic
14786            7, 0, // rule stop
14787            0, // non-greedy states
14788            0, // precedence states
14789            1, // rules
14790            0, // rule 0 start
14791            0, // modes
14792            0, // sets
14793            2, // transitions
14794            0, 1, 5, -1, 0, 0, // match EOF
14795            1, 2, 6, 0, 0, 0, // parser action
14796            0, // decisions
14797        ]))
14798        .deserialize_parser()
14799        .expect("artificial parser ATN should deserialize")
14800    }
14801
14802    fn noop_action_then_token_then_eof_atn() -> Atn {
14803        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14804            4, 1, 2, // version, parser, max token type
14805            4, // states
14806            2, 0, // rule start
14807            1, 0, // basic
14808            1, 0, // basic
14809            7, 0, // rule stop
14810            0, // non-greedy states
14811            0, // precedence states
14812            1, // rules
14813            0, // rule 0 start
14814            0, // modes
14815            0, // sets
14816            3, // transitions
14817            0, 1, 6, 0, -1, 0, // no-op parser action
14818            1, 2, 5, 1, 0, 0, // match token 1
14819            2, 3, 5, -1, 0, 0, // match EOF
14820            0, // decisions
14821        ]))
14822        .deserialize_parser()
14823        .expect("artificial no-op action ATN should deserialize")
14824    }
14825
14826    fn two_alt_decision_atn() -> Atn {
14827        let mut atn = ParserAtnBuilder::new(2);
14828        assert_eq!(
14829            atn.add_state(AtnStateKind::RuleStart, Some(0))
14830                .expect("state")
14831                .index(),
14832            0
14833        );
14834        assert_eq!(
14835            atn.add_state(AtnStateKind::BlockStart, Some(0))
14836                .expect("state")
14837                .index(),
14838            1
14839        );
14840        assert_eq!(
14841            atn.add_state(AtnStateKind::Basic, Some(0))
14842                .expect("state")
14843                .index(),
14844            2
14845        );
14846        assert_eq!(
14847            atn.add_state(AtnStateKind::Basic, Some(0))
14848                .expect("state")
14849                .index(),
14850            3
14851        );
14852        assert_eq!(
14853            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14854                .expect("state")
14855                .index(),
14856            4
14857        );
14858        assert_eq!(
14859            atn.add_state(AtnStateKind::RuleStop, Some(0))
14860                .expect("state")
14861                .index(),
14862            5
14863        );
14864        atn.set_rule_to_start_state(vec![0])
14865            .expect("rule start states");
14866        atn.set_rule_to_stop_state(vec![5])
14867            .expect("rule stop states");
14868        atn.add_decision_state(1).expect("decision state");
14869        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14870            .expect("transition");
14871        atn.add_transition(
14872            1,
14873            ParserTransitionSpec::Atom {
14874                target: 2,
14875                label: 1,
14876            },
14877        )
14878        .expect("transition");
14879        atn.add_transition(
14880            1,
14881            ParserTransitionSpec::Atom {
14882                target: 3,
14883                label: 2,
14884            },
14885        )
14886        .expect("transition");
14887        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
14888            .expect("transition");
14889        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
14890            .expect("transition");
14891        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14892            .expect("transition");
14893        finish_atn(atn)
14894    }
14895
14896    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
14897    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
14898    fn optional_then_b_eof_atn() -> Atn {
14899        let mut atn = ParserAtnBuilder::new(3);
14900        assert_eq!(
14901            atn.add_state(AtnStateKind::RuleStart, Some(0))
14902                .expect("state")
14903                .index(),
14904            0
14905        );
14906        assert_eq!(
14907            atn.add_state(AtnStateKind::BlockStart, Some(0))
14908                .expect("state")
14909                .index(),
14910            1
14911        );
14912        assert_eq!(
14913            atn.add_state(AtnStateKind::Basic, Some(0))
14914                .expect("state")
14915                .index(),
14916            2
14917        );
14918        assert_eq!(
14919            atn.add_state(AtnStateKind::Basic, Some(0))
14920                .expect("state")
14921                .index(),
14922            3
14923        );
14924        assert_eq!(
14925            atn.add_state(AtnStateKind::Basic, Some(0))
14926                .expect("state")
14927                .index(),
14928            4
14929        );
14930        assert_eq!(
14931            atn.add_state(AtnStateKind::RuleStop, Some(0))
14932                .expect("state")
14933                .index(),
14934            5
14935        );
14936        atn.set_rule_to_start_state(vec![0])
14937            .expect("rule start states");
14938        atn.set_rule_to_stop_state(vec![5])
14939            .expect("rule stop states");
14940        atn.add_decision_state(1).expect("decision state");
14941        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14942            .expect("transition");
14943        // Optional block: match A then fall through, or skip straight to state 3.
14944        atn.add_transition(
14945            1,
14946            ParserTransitionSpec::Atom {
14947                target: 3,
14948                label: 1,
14949            },
14950        )
14951        .expect("transition");
14952        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
14953            .expect("transition");
14954        // Match B, then EOF.
14955        atn.add_transition(
14956            3,
14957            ParserTransitionSpec::Atom {
14958                target: 4,
14959                label: 2,
14960            },
14961        )
14962        .expect("transition");
14963        atn.add_transition(
14964            4,
14965            ParserTransitionSpec::Atom {
14966                target: 5,
14967                label: TOKEN_EOF,
14968            },
14969        )
14970        .expect("transition");
14971        finish_atn(atn)
14972    }
14973
14974    #[test]
14975    fn sync_decision_deletes_only_a_single_token() {
14976        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
14977        // expected. `(A)? B EOF` at the optional-block decision:
14978        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
14979        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
14980        //               without consuming and records the expected set for the
14981        //               subsequent mismatch (the parser must not over-consume both
14982        //               `C`s and accept the input).
14983        let atn = optional_then_b_eof_atn();
14984
14985        let mut single = mini_parser(vec![
14986            TestToken::new(3).with_text("c"),
14987            TestToken::new(2).with_text("b"),
14988            TestToken::eof("parser-test", 1, 2, 2),
14989        ]);
14990        single.rule_context_stack = vec![RuleContextFrame {
14991            rule_index: 0,
14992            invoking_state: 0,
14993        }];
14994        let children = single
14995            .sync_decision(&atn, 1, true, false)
14996            .expect("single extraneous token recovers");
14997        assert_eq!(children.len(), 1);
14998        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
14999        assert_eq!(single.number_of_syntax_errors(), 1);
15000        // Exactly one token consumed (the cursor now sits on `b`).
15001        assert_eq!(single.la(1), 2);
15002
15003        let mut double = mini_parser(vec![
15004            TestToken::new(3).with_text("c"),
15005            TestToken::new(3).with_text("c"),
15006            TestToken::new(2).with_text("b"),
15007            TestToken::eof("parser-test", 1, 3, 3),
15008        ]);
15009        double.rule_context_stack = vec![RuleContextFrame {
15010            rule_index: 0,
15011            invoking_state: 0,
15012        }];
15013        let result = double.sync_decision(&atn, 1, true, false);
15014        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
15015        // consume either `c`. It reports the mismatch at the first `c` (so the parser
15016        // does not over-consume both and accept the input). Nothing is consumed, so
15017        // the cursor still sits on the first `c` for rule-level recovery.
15018        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
15019        match error {
15020            AntlrError::ParserError { message, .. } => {
15021                assert!(message.starts_with("mismatched input"), "got: {message}");
15022            }
15023            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
15024        }
15025        assert_eq!(double.la(1), 3);
15026    }
15027
15028    /// The real serialized ATN that `antlr4-rust-gen` emits for
15029    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
15030    /// the loop is `EOF`. The loop decision is state 5.
15031    fn star_loop_then_eof_atn() -> Atn {
15032        AtnDeserializer::new(&SerializedAtn::from_i32(&[
15033            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,
15034            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,
15035            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,
15036            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
15037        ]))
15038        .deserialize_parser()
15039        .expect("star-loop-then-EOF ATN should deserialize")
15040    }
15041
15042    /// ATN for `s : a+ Y ; a : X ;`.
15043    ///
15044    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
15045    /// `+` loop must not treat that zero-width child as a successful iteration
15046    /// and then re-enter the loop at the same token index.
15047    fn plus_loop_with_recovering_body_atn() -> Atn {
15048        let mut atn = ParserAtnBuilder::new(2);
15049        assert_eq!(
15050            atn.add_state(AtnStateKind::RuleStart, Some(0))
15051                .expect("state")
15052                .index(),
15053            0
15054        );
15055        assert_eq!(
15056            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
15057                .expect("state")
15058                .index(),
15059            1
15060        );
15061        assert_eq!(
15062            atn.add_state(AtnStateKind::Basic, Some(0))
15063                .expect("state")
15064                .index(),
15065            2
15066        );
15067        assert_eq!(
15068            atn.add_state(AtnStateKind::BlockEnd, Some(0))
15069                .expect("state")
15070                .index(),
15071            3
15072        );
15073        assert_eq!(
15074            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
15075                .expect("state")
15076                .index(),
15077            4
15078        );
15079        assert_eq!(
15080            atn.add_state(AtnStateKind::LoopEnd, Some(0))
15081                .expect("state")
15082                .index(),
15083            5
15084        );
15085        assert_eq!(
15086            atn.add_state(AtnStateKind::RuleStop, Some(0))
15087                .expect("state")
15088                .index(),
15089            6
15090        );
15091        assert_eq!(
15092            atn.add_state(AtnStateKind::RuleStart, Some(1))
15093                .expect("state")
15094                .index(),
15095            7
15096        );
15097        assert_eq!(
15098            atn.add_state(AtnStateKind::Basic, Some(1))
15099                .expect("state")
15100                .index(),
15101            8
15102        );
15103        assert_eq!(
15104            atn.add_state(AtnStateKind::RuleStop, Some(1))
15105                .expect("state")
15106                .index(),
15107            9
15108        );
15109        atn.set_rule_to_start_state(vec![0, 7])
15110            .expect("rule start states");
15111        atn.set_rule_to_stop_state(vec![6, 9])
15112            .expect("rule stop states");
15113        atn.set_end_state(1, 3).expect("block end state");
15114        atn.set_loop_back_state(5, 4).expect("loop back state");
15115        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15116            .expect("transition");
15117        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15118            .expect("transition");
15119        atn.add_transition(
15120            2,
15121            ParserTransitionSpec::Rule {
15122                target: 7,
15123                rule_index: 1,
15124                follow_state: 3,
15125                precedence: 0,
15126            },
15127        )
15128        .expect("transition");
15129        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15130            .expect("transition");
15131        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
15132            .expect("transition");
15133        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15134            .expect("transition");
15135        atn.add_transition(
15136            5,
15137            ParserTransitionSpec::Atom {
15138                target: 6,
15139                label: 2,
15140            },
15141        )
15142        .expect("transition");
15143        atn.add_transition(
15144            7,
15145            ParserTransitionSpec::Atom {
15146                target: 8,
15147                label: 1,
15148            },
15149        )
15150        .expect("transition");
15151        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15152            .expect("transition");
15153        finish_atn(atn)
15154    }
15155
15156    #[test]
15157    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
15158        let atn = plus_loop_with_recovering_body_atn();
15159        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15160
15161        let error = parser
15162            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15163            .expect_err("EOF recovery should report a bounded mismatch");
15164
15165        let AntlrError::ParserError { message, .. } = error else {
15166            panic!("expected ParserError, got {error:?}");
15167        };
15168        insta::assert_snapshot!(message, @"mismatched input '<EOF>' expecting {'x', 2}");
15169        assert_eq!(parser.number_of_syntax_errors(), 1);
15170        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
15171    }
15172
15173    #[test]
15174    fn sync_decision_deletes_token_before_eof_at_loop_back() {
15175        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
15176        // At the loop ENTRY (loop_back = false) a single unexpected token before
15177        // EOF is deleted as an error node (then the generated EOF match consumes
15178        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
15179        // EOF must be a valid scan-stop for this to fire.
15180        let atn = star_loop_then_eof_atn();
15181        let mut parser = mini_parser(vec![
15182            TestToken::new(2).with_text("c"),
15183            TestToken::eof("parser-test", 1, 1, 1),
15184        ]);
15185        parser.rule_context_stack = vec![RuleContextFrame {
15186            rule_index: 0,
15187            invoking_state: 0,
15188        }];
15189        let children = parser
15190            .sync_decision(&atn, 5, true, false)
15191            .expect("single token before EOF recovers");
15192        assert_eq!(children.len(), 1);
15193        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
15194        assert_eq!(parser.number_of_syntax_errors(), 1);
15195        assert_eq!(
15196            parser.la(1),
15197            TOKEN_EOF,
15198            "EOF is left for the rule's EOF match"
15199        );
15200    }
15201
15202    #[test]
15203    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
15204        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
15205        // single-token deletion, which fails because LA(2) = `c` is not expected —
15206        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
15207        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
15208        let atn = star_loop_then_eof_atn();
15209        let mut parser = mini_parser(vec![
15210            TestToken::new(2).with_text("c"),
15211            TestToken::new(2).with_text("c"),
15212            TestToken::eof("parser-test", 1, 2, 2),
15213        ]);
15214        parser.rule_context_stack = vec![RuleContextFrame {
15215            rule_index: 0,
15216            invoking_state: 0,
15217        }];
15218        let error = parser
15219            .sync_decision(&atn, 5, true, false)
15220            .expect_err("two tokens at the loop entry must not be deleted");
15221        match error {
15222            AntlrError::ParserError { message, .. } => {
15223                assert!(message.starts_with("mismatched input"), "got: {message}");
15224            }
15225            other => panic!("expected mismatched-input ParserError, got {other:?}"),
15226        }
15227        assert_eq!(
15228            parser.la(1),
15229            2,
15230            "nothing consumed; cursor still on first `c`"
15231        );
15232    }
15233
15234    #[test]
15235    fn sync_decision_consumes_until_eof_at_loop_back() {
15236        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
15237        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
15238        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
15239        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
15240        // post-`a` state directly: `c c <EOF>` with loop_back = true.
15241        let atn = star_loop_then_eof_atn();
15242        let mut parser = mini_parser(vec![
15243            TestToken::new(2).with_text("c"),
15244            TestToken::new(2).with_text("c"),
15245            TestToken::eof("parser-test", 1, 2, 2),
15246        ]);
15247        parser.rule_context_stack = vec![RuleContextFrame {
15248            rule_index: 0,
15249            invoking_state: 0,
15250        }];
15251        let children = parser
15252            .sync_decision(&atn, 5, false, true)
15253            .expect("loop-back multi-token deletion recovers onto EOF");
15254        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
15255        assert!(
15256            children
15257                .iter()
15258                .all(|child| parser.node(*child).kind() == NodeKind::Error)
15259        );
15260        assert_eq!(parser.number_of_syntax_errors(), 1);
15261        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
15262    }
15263
15264    fn predicate_after_token_atn() -> Atn {
15265        let mut atn = ParserAtnBuilder::new(2);
15266        assert_eq!(
15267            atn.add_state(AtnStateKind::RuleStart, Some(0))
15268                .expect("state")
15269                .index(),
15270            0
15271        );
15272        assert_eq!(
15273            atn.add_state(AtnStateKind::Basic, Some(0))
15274                .expect("state")
15275                .index(),
15276            1
15277        );
15278        assert_eq!(
15279            atn.add_state(AtnStateKind::Basic, Some(0))
15280                .expect("state")
15281                .index(),
15282            2
15283        );
15284        assert_eq!(
15285            atn.add_state(AtnStateKind::Basic, Some(0))
15286                .expect("state")
15287                .index(),
15288            3
15289        );
15290        assert_eq!(
15291            atn.add_state(AtnStateKind::RuleStop, Some(0))
15292                .expect("state")
15293                .index(),
15294            4
15295        );
15296        atn.set_rule_to_start_state(vec![0])
15297            .expect("rule start states");
15298        atn.set_rule_to_stop_state(vec![4])
15299            .expect("rule stop states");
15300        atn.add_transition(
15301            0,
15302            ParserTransitionSpec::Atom {
15303                target: 1,
15304                label: 1,
15305            },
15306        )
15307        .expect("transition");
15308        atn.add_transition(
15309            1,
15310            ParserTransitionSpec::Predicate {
15311                target: 2,
15312                rule_index: 0,
15313                pred_index: 0,
15314                context_dependent: false,
15315            },
15316        )
15317        .expect("transition");
15318        atn.add_transition(
15319            2,
15320            ParserTransitionSpec::Atom {
15321                target: 3,
15322                label: 2,
15323            },
15324        )
15325        .expect("transition");
15326        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15327            .expect("transition");
15328        finish_atn(atn)
15329    }
15330
15331    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
15332        let mut atn = ParserAtnBuilder::new(1);
15333        for (state_number, kind) in [
15334            (0, AtnStateKind::RuleStart),
15335            (1, AtnStateKind::BlockStart),
15336            (2, AtnStateKind::Basic),
15337            (3, AtnStateKind::Basic),
15338            (4, AtnStateKind::Basic),
15339            (5, AtnStateKind::Basic),
15340            (6, AtnStateKind::BlockEnd),
15341            (7, AtnStateKind::RuleStop),
15342        ] {
15343            assert_eq!(
15344                atn.add_state(kind, Some(0)).expect("state").index(),
15345                state_number
15346            );
15347        }
15348        atn.set_rule_to_start_state(vec![0])
15349            .expect("rule start states");
15350        atn.set_rule_to_stop_state(vec![7])
15351            .expect("rule stop states");
15352        atn.add_decision_state(1).expect("decision state");
15353        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15354            .expect("transition");
15355        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15356            .expect("transition");
15357        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
15358            .expect("transition");
15359        atn.add_transition(
15360            2,
15361            ParserTransitionSpec::Predicate {
15362                target: 4,
15363                rule_index: 0,
15364                pred_index: pred_indexes[0],
15365                context_dependent: false,
15366            },
15367        )
15368        .expect("transition");
15369        atn.add_transition(
15370            3,
15371            ParserTransitionSpec::Predicate {
15372                target: 5,
15373                rule_index: 0,
15374                pred_index: pred_indexes[1],
15375                context_dependent: false,
15376            },
15377        )
15378        .expect("transition");
15379        atn.add_transition(
15380            4,
15381            ParserTransitionSpec::Atom {
15382                target: 6,
15383                label: 1,
15384            },
15385        )
15386        .expect("transition");
15387        atn.add_transition(
15388            5,
15389            ParserTransitionSpec::Atom {
15390                target: 6,
15391                label: 1,
15392            },
15393        )
15394        .expect("transition");
15395        atn.add_transition(
15396            6,
15397            ParserTransitionSpec::Atom {
15398                target: 7,
15399                label: TOKEN_EOF,
15400            },
15401        )
15402        .expect("transition");
15403        finish_atn(atn)
15404    }
15405
15406    fn nested_nullable_context_atn() -> Atn {
15407        let mut atn = ParserAtnBuilder::new(1);
15408        for state_number in 0..=20 {
15409            let kind = match state_number {
15410                0 | 10 | 16 => AtnStateKind::RuleStart,
15411                9 | 15 | 20 => AtnStateKind::RuleStop,
15412                _ => AtnStateKind::Basic,
15413            };
15414            let rule_index = match state_number {
15415                0..=9 => 0,
15416                10..=15 => 1,
15417                _ => 2,
15418            };
15419            assert_eq!(
15420                atn.add_state(kind, Some(rule_index))
15421                    .expect("state")
15422                    .index(),
15423                state_number
15424            );
15425        }
15426        atn.set_rule_to_start_state(vec![0, 10, 16])
15427            .expect("rule start states");
15428        atn.set_rule_to_stop_state(vec![9, 15, 20])
15429            .expect("rule stop states");
15430        atn.add_transition(
15431            1,
15432            ParserTransitionSpec::Rule {
15433                target: 10,
15434                rule_index: 1,
15435                follow_state: 8,
15436                precedence: 0,
15437            },
15438        )
15439        .expect("transition");
15440        atn.add_transition(
15441            8,
15442            ParserTransitionSpec::Atom {
15443                target: 9,
15444                label: 1,
15445            },
15446        )
15447        .expect("transition");
15448        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15449            .expect("transition");
15450        atn.add_transition(
15451            2,
15452            ParserTransitionSpec::Rule {
15453                target: 16,
15454                rule_index: 2,
15455                follow_state: 14,
15456                precedence: 0,
15457            },
15458        )
15459        .expect("transition");
15460        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
15461            .expect("transition");
15462        finish_atn(atn)
15463    }
15464
15465    fn generated_match_recovery_atn() -> Atn {
15466        let mut atn = ParserAtnBuilder::new(2);
15467        assert_eq!(
15468            atn.add_state(AtnStateKind::RuleStart, Some(0))
15469                .expect("state")
15470                .index(),
15471            0
15472        );
15473        assert_eq!(
15474            atn.add_state(AtnStateKind::Basic, Some(0))
15475                .expect("state")
15476                .index(),
15477            1
15478        );
15479        assert_eq!(
15480            atn.add_state(AtnStateKind::Basic, Some(0))
15481                .expect("state")
15482                .index(),
15483            2
15484        );
15485        assert_eq!(
15486            atn.add_state(AtnStateKind::RuleStop, Some(0))
15487                .expect("state")
15488                .index(),
15489            3
15490        );
15491        assert_eq!(
15492            atn.add_state(AtnStateKind::RuleStart, Some(1))
15493                .expect("state")
15494                .index(),
15495            4
15496        );
15497        assert_eq!(
15498            atn.add_state(AtnStateKind::RuleStop, Some(1))
15499                .expect("state")
15500                .index(),
15501            5
15502        );
15503        atn.set_rule_to_start_state(vec![0, 4])
15504            .expect("rule start states");
15505        atn.set_rule_to_stop_state(vec![3, 5])
15506            .expect("rule stop states");
15507        atn.add_transition(
15508            1,
15509            ParserTransitionSpec::Rule {
15510                target: 4,
15511                rule_index: 1,
15512                follow_state: 2,
15513                precedence: 0,
15514            },
15515        )
15516        .expect("transition");
15517        atn.add_transition(
15518            2,
15519            ParserTransitionSpec::Atom {
15520                target: 3,
15521                label: TOKEN_EOF,
15522            },
15523        )
15524        .expect("transition");
15525        finish_atn(atn)
15526    }
15527
15528    fn complement_set_atn() -> Atn {
15529        let mut atn = ParserAtnBuilder::new(1);
15530        assert_eq!(
15531            atn.add_state(AtnStateKind::RuleStart, Some(0))
15532                .expect("state")
15533                .index(),
15534            0
15535        );
15536        assert_eq!(
15537            atn.add_state(AtnStateKind::RuleStop, Some(0))
15538                .expect("state")
15539                .index(),
15540            1
15541        );
15542        atn.set_rule_to_start_state(vec![0])
15543            .expect("rule start states");
15544        atn.set_rule_to_stop_state(vec![1])
15545            .expect("rule stop states");
15546        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
15547        atn.add_transition(
15548            0,
15549            ParserTransitionSpec::NotSet {
15550                target: 1,
15551                set: excluded,
15552            },
15553        )
15554        .expect("transition");
15555        finish_atn(atn)
15556    }
15557
15558    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
15559    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
15560    fn wildcard_then_eof_atn() -> Atn {
15561        let mut atn = ParserAtnBuilder::new(1);
15562        assert_eq!(
15563            atn.add_state(AtnStateKind::RuleStart, Some(0))
15564                .expect("state")
15565                .index(),
15566            0
15567        );
15568        assert_eq!(
15569            atn.add_state(AtnStateKind::RuleStop, Some(0))
15570                .expect("state")
15571                .index(),
15572            1
15573        );
15574        assert_eq!(
15575            atn.add_state(AtnStateKind::Basic, Some(0))
15576                .expect("state")
15577                .index(),
15578            2
15579        );
15580        atn.set_rule_to_start_state(vec![0])
15581            .expect("rule start states");
15582        atn.set_rule_to_stop_state(vec![1])
15583            .expect("rule stop states");
15584        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
15585            .expect("transition");
15586        atn.add_transition(
15587            2,
15588            ParserTransitionSpec::Atom {
15589                target: 1,
15590                label: TOKEN_EOF,
15591            },
15592        )
15593        .expect("transition");
15594        finish_atn(atn)
15595    }
15596
15597    #[test]
15598    fn parser_matches_token_and_reports_mismatch() {
15599        let source = Source {
15600            tokens: vec![
15601                TestToken::new(1).with_text("x"),
15602                TestToken::eof("parser-test", 1, 1, 1),
15603            ],
15604            index: 0,
15605        };
15606        let data = RecognizerData::new(
15607            "Mini.g4",
15608            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15609        );
15610        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
15611        let matched = parser.match_token(1).expect("token 1 should match");
15612        assert_eq!(parser.node(matched).text(), "x");
15613        assert!(parser.match_token(1).is_err());
15614    }
15615
15616    #[test]
15617    fn parser_matches_token_sets() {
15618        let mut parser = mini_parser(vec![
15619            TestToken::new(1).with_text("x"),
15620            TestToken::eof("parser-test", 1, 1, 1),
15621        ]);
15622
15623        let matched = parser
15624            .match_set(&[(1, 1), (3, 4)])
15625            .expect("token set should match");
15626        assert_eq!(parser.node(matched).text(), "x");
15627        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
15628    }
15629
15630    #[test]
15631    fn generated_rule_api_tracks_state_and_precedence() {
15632        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15633
15634        let context = parser.enter_rule(7, 2);
15635        assert_eq!(context.rule_index(), 2);
15636        assert_eq!(parser.state(), 7);
15637        assert_eq!(
15638            parser.rule_context_stack,
15639            vec![RuleContextFrame {
15640                rule_index: 2,
15641                invoking_state: 7
15642            }]
15643        );
15644
15645        let recursive = parser.enter_recursion_rule(11, 3, 4);
15646        assert_eq!(recursive.rule_index(), 3);
15647        assert!(parser.precpred(4));
15648        assert!(parser.precpred(5));
15649        assert!(!parser.precpred(3));
15650
15651        let next = parser.push_new_recursion_context(13, 3);
15652        assert_eq!(next.invoking_state(), 13);
15653        parser.unroll_recursion_context();
15654        assert_eq!(parser.precedence_stack, vec![0]);
15655        assert_eq!(
15656            parser.rule_context_stack,
15657            vec![RuleContextFrame {
15658                rule_index: 2,
15659                invoking_state: 7
15660            }]
15661        );
15662
15663        parser.exit_rule();
15664        assert!(parser.rule_context_stack.is_empty());
15665    }
15666
15667    #[test]
15668    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
15669        let mut parser = mini_parser(vec![
15670            TestToken::new(1).with_text("x"),
15671            TestToken::eof("parser-test", 1, 1, 1),
15672        ]);
15673        let matched = parser.match_token(1).expect("token should match");
15674        assert_eq!(parser.node(matched).text(), "x");
15675        parser.record_generated_syntax_error();
15676        parser.set_int_member(7, 11);
15677        parser.set_build_parse_trees(false);
15678        parser.set_report_diagnostic_errors(true);
15679        parser.set_prediction_mode(PredictionMode::Sll);
15680        parser.set_bail_on_error(true);
15681        let _context = parser.enter_recursion_rule(9, 0, 4);
15682        parser.pending_invoking_states.push(5);
15683        parser.unknown_predicate_hits.push((0, 1));
15684        parser.unhandled_action_hits.push((0, 2));
15685
15686        parser.reset();
15687
15688        assert_eq!(parser.input.index(), 0);
15689        assert_eq!(parser.la(1), 1);
15690        assert_eq!(parser.state(), -1);
15691        assert_eq!(parser.number_of_syntax_errors(), 0);
15692        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15693        assert!(parser.rule_context_stack.is_empty());
15694        assert!(parser.pending_invoking_states.is_empty());
15695        assert_eq!(parser.precedence_stack, [0]);
15696        assert!(parser.unknown_predicate_hits.is_empty());
15697        assert!(parser.unhandled_action_hits.is_empty());
15698        assert_eq!(parser.int_member(7), Some(11));
15699        assert!(!parser.build_parse_trees());
15700        assert!(parser.report_diagnostic_errors());
15701        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
15702        assert!(parser.bail_on_error());
15703    }
15704
15705    #[test]
15706    fn set_token_stream_replaces_input_and_resets_parser() {
15707        let mut parser = mini_parser(vec![
15708            TestToken::new(1).with_text("old"),
15709            TestToken::eof("parser-test", 1, 1, 1),
15710        ]);
15711        parser.consume();
15712        parser.record_generated_syntax_error();
15713        let replacement = CommonTokenStream::new(Source {
15714            tokens: vec![
15715                TestToken::new(2).with_text("new"),
15716                TestToken::eof("parser-test", 1, 1, 1),
15717            ],
15718            index: 0,
15719        });
15720
15721        parser.set_token_stream(replacement);
15722
15723        assert_eq!(parser.input.index(), 0);
15724        assert_eq!(parser.la(1), 2);
15725        assert_eq!(parser.input.text_all(), "new");
15726        assert_eq!(parser.number_of_syntax_errors(), 0);
15727    }
15728
15729    #[test]
15730    fn active_invocation_states_exclude_the_root_frame() {
15731        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15732
15733        let _root = parser.enter_rule(0, 0);
15734        assert!(parser.active_invocation_states().is_empty());
15735
15736        let marker = parser.push_invoking_state(6);
15737        let _child = parser.enter_rule(2, 1);
15738        parser.discard_invoking_state(marker);
15739        assert_eq!(parser.active_invocation_states(), [6]);
15740
15741        let marker = parser.push_invoking_state(13);
15742        let _grandchild = parser.enter_rule(4, 2);
15743        parser.discard_invoking_state(marker);
15744        assert_eq!(parser.active_invocation_states(), [13, 6]);
15745
15746        parser.exit_rule();
15747        parser.exit_rule();
15748        parser.exit_rule();
15749    }
15750
15751    #[test]
15752    fn parser_predicates_support_token_adjacency() {
15753        let mut parser = mini_parser(vec![
15754            TestToken::new(1).with_text("=").with_span(0, 0),
15755            TestToken::new(1).with_text(">").with_span(1, 1),
15756            TestToken::eof("parser-test", 2, 1, 2),
15757        ]);
15758        parser.consume();
15759        parser.consume();
15760
15761        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
15762
15763        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15764
15765        let mut parser = mini_parser(vec![
15766            TestToken::new(1).with_text("=").with_span(0, 0),
15767            TestToken::new(1)
15768                .with_text(" ")
15769                .with_channel(HIDDEN_CHANNEL)
15770                .with_span(1, 1),
15771            TestToken::new(1).with_text(">").with_span(2, 2),
15772            TestToken::eof("parser-test", 3, 1, 3),
15773        ]);
15774        parser.consume();
15775        parser.consume();
15776
15777        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15778    }
15779
15780    #[test]
15781    fn parser_predicates_support_context_child_text_checks() {
15782        let mut parser = mini_parser(vec![
15783            TestToken::new(1).with_text("var"),
15784            TestToken::eof("parser-test", 1, 1, 1),
15785        ]);
15786        let mut context = ParserRuleContext::new(1, 0);
15787        let mut child_context = ParserRuleContext::new(2, 0);
15788        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
15789        parser.tree.add_child(&mut child_context, terminal);
15790        let child = parser.rule_node(child_context);
15791        parser.tree.add_child(&mut context, child);
15792        let predicates = [(
15793            1,
15794            0,
15795            ParserPredicate::ContextChildRuleTextNotEquals {
15796                rule_index: 2,
15797                text: "var",
15798            },
15799        )];
15800
15801        assert!(
15802            !parser.parser_semantic_predicate_matches_with_context_and_local(
15803                &predicates,
15804                1,
15805                0,
15806                &context,
15807                0,
15808            )
15809        );
15810    }
15811
15812    #[test]
15813    fn context_expected_symbols_walks_nullable_parent_contexts() {
15814        let atn = nested_nullable_context_atn();
15815        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15816        parser.rule_context_stack = vec![
15817            RuleContextFrame {
15818                rule_index: 0,
15819                invoking_state: 0,
15820            },
15821            RuleContextFrame {
15822                rule_index: 1,
15823                invoking_state: 1,
15824            },
15825            RuleContextFrame {
15826                rule_index: 2,
15827                invoking_state: 2,
15828            },
15829        ];
15830
15831        let expected = parser.context_expected_symbols(&atn);
15832
15833        assert!(expected.contains(&1));
15834        assert!(expected.contains(&TOKEN_EOF));
15835    }
15836
15837    #[test]
15838    fn prediction_context_return_states_track_rule_stack_changes() {
15839        let atn = nested_nullable_context_atn();
15840        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15841        parser.rule_context_stack = vec![
15842            RuleContextFrame {
15843                rule_index: 0,
15844                invoking_state: 0,
15845            },
15846            RuleContextFrame {
15847                rule_index: 1,
15848                invoking_state: 1,
15849            },
15850            RuleContextFrame {
15851                rule_index: 2,
15852                invoking_state: 2,
15853            },
15854        ];
15855
15856        let initial_version = parser.rule_context_version();
15857        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15858        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15859        assert_eq!(first, second);
15860        assert_eq!(parser.rule_context_version(), initial_version);
15861
15862        parser.exit_rule();
15863        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15864        assert_ne!(first, after_pop);
15865        assert_ne!(parser.rule_context_version(), initial_version);
15866    }
15867
15868    #[test]
15869    fn generated_match_token_recovers_missing_token_from_context_follow() {
15870        let atn = generated_match_recovery_atn();
15871        let data = RecognizerData::new(
15872            "Mini.g4",
15873            Vocabulary::new(
15874                [None, Some("'X'"), Some("'Y'")],
15875                [None, Some("X"), Some("Y")],
15876                [None::<&str>, None, None],
15877            ),
15878        );
15879        let mut parser = BaseParser::new(
15880            CommonTokenStream::new(Source {
15881                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
15882                index: 0,
15883            }),
15884            data,
15885        );
15886        parser.rule_context_stack = vec![
15887            RuleContextFrame {
15888                rule_index: 0,
15889                invoking_state: 0,
15890            },
15891            RuleContextFrame {
15892                rule_index: 1,
15893                invoking_state: 1,
15894            },
15895        ];
15896        assert_eq!(parser.number_of_syntax_errors(), 0);
15897
15898        let node = parser
15899            .match_token_recovering(2, 5, &atn)
15900            .expect("generated match should insert missing token");
15901
15902        assert_eq!(node.children().len(), 1);
15903        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
15904        assert_eq!(
15905            node.clone()
15906                .into_child_iter()
15907                .map(|child| parser.node(child).text())
15908                .collect::<Vec<_>>(),
15909            ["<missing 'Y'>"]
15910        );
15911        // Single-token insertion synthesizes a missing token and consumes nothing,
15912        // so no EOF terminal is consumed even though lookahead is EOF.
15913        assert!(!node.consumed_eof());
15914        assert_eq!(parser.la(1), TOKEN_EOF);
15915        assert_eq!(parser.number_of_syntax_errors(), 1);
15916        assert_eq!(
15917            parser.generated_parser_diagnostics,
15918            [ParserDiagnostic {
15919                line: 1,
15920                column: 3,
15921                message: "missing 'Y' at '<EOF>'".to_owned(),
15922                offending: parser.input.lt_id(1),
15923            }]
15924        );
15925    }
15926
15927    #[test]
15928    fn generated_match_token_counts_single_token_deletion_recovery() {
15929        let atn = generated_match_recovery_atn();
15930        let data = RecognizerData::new(
15931            "Mini.g4",
15932            Vocabulary::new(
15933                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
15934                [None, Some("X"), Some("Y"), Some("Z")],
15935                [None::<&str>, None, None, None],
15936            ),
15937        );
15938        let mut parser = BaseParser::new(
15939            CommonTokenStream::new(Source {
15940                tokens: vec![
15941                    TestToken::new(3).with_text("z"),
15942                    TestToken::new(2).with_text("y"),
15943                    TestToken::eof("parser-test", 3, 1, 3),
15944                ],
15945                index: 0,
15946            }),
15947            data,
15948        );
15949
15950        let node = parser
15951            .match_token_recovering(2, 5, &atn)
15952            .expect("generated match should delete the extraneous token");
15953
15954        assert_eq!(node.children().len(), 2);
15955        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
15956        assert_eq!(parser.node(node.children()[0]).text(), "z");
15957        assert_eq!(parser.node(node.children()[1]).text(), "y");
15958        assert_eq!(
15959            node.into_child_iter()
15960                .map(|child| parser.node(child).text())
15961                .collect::<Vec<_>>(),
15962            ["z", "y"]
15963        );
15964        assert_eq!(parser.number_of_syntax_errors(), 1);
15965    }
15966
15967    #[test]
15968    fn generated_match_token_iterates_single_success_without_a_children_vec() {
15969        let atn = generated_match_recovery_atn();
15970        let data = RecognizerData::new(
15971            "Mini.g4",
15972            Vocabulary::new(
15973                [None, Some("'X'"), Some("'Y'")],
15974                [None, Some("X"), Some("Y")],
15975                [None::<&str>, None, None],
15976            ),
15977        );
15978        let mut parser = BaseParser::new(
15979            CommonTokenStream::new(Source {
15980                tokens: vec![
15981                    TestToken::new(2).with_text("y"),
15982                    TestToken::eof("parser-test", 1, 1, 1),
15983                ],
15984                index: 0,
15985            }),
15986            data,
15987        );
15988
15989        let node = parser
15990            .match_token_recovering(2, 5, &atn)
15991            .expect("generated match should consume the expected token");
15992
15993        assert_eq!(
15994            node.into_child_iter()
15995                .map(|child| parser.node(child).text())
15996                .collect::<Vec<_>>(),
15997            ["y"]
15998        );
15999        assert_eq!(parser.number_of_syntax_errors(), 0);
16000    }
16001
16002    #[test]
16003    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
16004        let atn = generated_match_recovery_atn();
16005        let data = RecognizerData::new(
16006            "Mini.g4",
16007            Vocabulary::new(
16008                [None, Some("'X'"), Some("'Y'")],
16009                [None, Some("X"), Some("Y")],
16010                [None::<&str>, None, None],
16011            ),
16012        );
16013        let mut parser = BaseParser::new(
16014            CommonTokenStream::new(Source {
16015                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
16016                index: 0,
16017            }),
16018            data,
16019        );
16020        parser.rule_context_stack = vec![
16021            RuleContextFrame {
16022                rule_index: 0,
16023                invoking_state: 0,
16024            },
16025            RuleContextFrame {
16026                rule_index: 1,
16027                invoking_state: 1,
16028            },
16029        ];
16030        let marker = parser.generated_diagnostics_checkpoint();
16031
16032        let _ = parser
16033            .match_token_recovering(2, 5, &atn)
16034            .expect("generated match should insert missing token");
16035        assert_eq!(parser.number_of_syntax_errors(), 1);
16036
16037        parser.restore_generated_diagnostics(marker);
16038
16039        assert_eq!(parser.number_of_syntax_errors(), 0);
16040        assert!(parser.generated_parser_diagnostics.is_empty());
16041    }
16042
16043    #[test]
16044    fn generated_prediction_diagnostics_use_adaptive_context() {
16045        let atn = two_alt_decision_atn();
16046        let data = RecognizerData::new(
16047            "Mini.g4",
16048            Vocabulary::new(
16049                [None, Some("'x'"), Some("'y'")],
16050                [None, Some("X"), Some("Y")],
16051                [None::<&str>, None, None],
16052            ),
16053        )
16054        .with_rule_names(["s"]);
16055        let mut parser = BaseParser::new(
16056            CommonTokenStream::new(Source {
16057                tokens: vec![
16058                    TestToken::new(1)
16059                        .with_text("x")
16060                        .with_position(1, 0)
16061                        .with_span(0, 0),
16062                    TestToken::new(2)
16063                        .with_text("y")
16064                        .with_position(1, 2)
16065                        .with_span(1, 1),
16066                    TestToken::eof("parser-test", 2, 1, 3),
16067                ],
16068                index: 0,
16069            }),
16070            data,
16071        );
16072        parser.set_report_diagnostic_errors(true);
16073
16074        parser.record_generated_prediction_diagnostic(
16075            &atn,
16076            1,
16077            &ParserAtnPrediction {
16078                alt: 1,
16079                requires_full_context: true,
16080                has_semantic_context: false,
16081                diagnostic: Some(ParserAtnPredictionDiagnostic {
16082                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
16083                    start_index: 0,
16084                    sll_stop_index: 1,
16085                    ll_stop_index: 0,
16086                    conflicting_alts: vec![1, 2],
16087                    exact: false,
16088                }),
16089            },
16090        );
16091        // Ambiguities from the default LL prediction mode are non-exact, so —
16092        // matching Java's exactOnly DiagnosticErrorListener — only the
16093        // attempting-full-context line is reported. Exact-ambiguity mode
16094        // reports the ambiguity itself.
16095        parser.record_generated_prediction_diagnostic(
16096            &atn,
16097            1,
16098            &ParserAtnPrediction {
16099                alt: 1,
16100                requires_full_context: true,
16101                has_semantic_context: false,
16102                diagnostic: Some(ParserAtnPredictionDiagnostic {
16103                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
16104                    start_index: 0,
16105                    sll_stop_index: 1,
16106                    ll_stop_index: 1,
16107                    conflicting_alts: vec![1, 2],
16108                    exact: false,
16109                }),
16110            },
16111        );
16112
16113        // The full-context/context-sensitivity diagnostic trace (order + decision + input windows)
16114        // is one snapshot rather than three ParserDiagnostic literals.
16115        insta::assert_debug_snapshot!(
16116            "generated_prediction_diagnostics_use_adaptive_context",
16117            parser.generated_parser_diagnostics
16118        );
16119    }
16120
16121    #[test]
16122    fn generated_match_not_set_recovers_empty_complement_at_eof() {
16123        let atn = complement_set_atn();
16124        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16125        parser.rule_context_stack = vec![RuleContextFrame {
16126            rule_index: 0,
16127            invoking_state: 0,
16128        }];
16129
16130        let node = parser
16131            .match_not_token_set_recovering(
16132                atn.token_set(0).expect("excluded token set"),
16133                1,
16134                1,
16135                1,
16136                &atn,
16137            )
16138            .expect("empty complement should recover at EOF");
16139
16140        assert_eq!(node.children().len(), 1);
16141        // Recovery synthesizes a missing token without consuming EOF, so the
16142        // enclosing rule must not record EOF as its stop token.
16143        assert!(!node.consumed_eof());
16144        assert_eq!(parser.la(1), TOKEN_EOF);
16145        assert_eq!(
16146            parser.generated_parser_diagnostics,
16147            [ParserDiagnostic {
16148                line: 1,
16149                column: 1,
16150                message: "missing {} at '<EOF>'".to_owned(),
16151                offending: parser.input.lt_id(1),
16152            }]
16153        );
16154    }
16155
16156    #[test]
16157    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
16158        // `start : . EOF ;` on empty input. The wildcard is modeled as an
16159        // empty-complement not-set; at EOF the follow state (the explicit EOF
16160        // match) expects EOF, so even in the start rule recovery must perform
16161        // single-token insertion (`<missing ...>`) rather than aborting — matching
16162        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
16163        let atn = wildcard_then_eof_atn();
16164        let data = RecognizerData::new(
16165            "Mini.g4",
16166            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16167        );
16168        let mut parser = BaseParser::new(
16169            CommonTokenStream::new(Source {
16170                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
16171                index: 0,
16172            }),
16173            data,
16174        );
16175        parser.rule_context_stack = vec![RuleContextFrame {
16176            rule_index: 0,
16177            invoking_state: 0,
16178        }];
16179
16180        let node = parser
16181            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
16182            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
16183
16184        // A single `<missing ...>` error node is inserted; EOF is not consumed.
16185        assert_eq!(node.children().len(), 1);
16186        assert!(!node.consumed_eof());
16187        assert!(
16188            parser
16189                .node(node.children()[0])
16190                .text()
16191                .starts_with("<missing")
16192        );
16193        assert_eq!(parser.la(1), TOKEN_EOF);
16194        assert_eq!(
16195            parser.generated_parser_diagnostics,
16196            [ParserDiagnostic {
16197                line: 1,
16198                column: 1,
16199                message: "missing 'x' at '<EOF>'".to_owned(),
16200                offending: parser.input.lt_id(1),
16201            }]
16202        );
16203    }
16204
16205    #[test]
16206    fn generated_rule_recovery_consumes_to_parent_follow() {
16207        let atn = generated_match_recovery_atn();
16208        let data = RecognizerData::new(
16209            "Mini.g4",
16210            Vocabulary::new(
16211                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
16212                [None, Some("X"), Some("Y"), Some("Z")],
16213                [None::<&str>, None, None, None],
16214            ),
16215        );
16216        let mut parser = BaseParser::new(
16217            CommonTokenStream::new(Source {
16218                tokens: vec![
16219                    TestToken::new(3).with_text("z"),
16220                    TestToken::eof("parser-test", 1, 1, 1),
16221                ],
16222                index: 0,
16223            }),
16224            data,
16225        );
16226        let _parent = parser.enter_rule(0, 0);
16227        let marker = parser.push_invoking_state(1);
16228        let mut child = parser.enter_rule(4, 1);
16229        parser.discard_invoking_state(marker);
16230
16231        // The anchor recorded where the error was built must survive into the
16232        // dispatched diagnostic even though recovery consumes past it below.
16233        let offending = parser.input.lt_id(1);
16234        assert!(offending.is_some(), "the 'z' token should be buffered");
16235        parser.recover_generated_rule(
16236            &mut child,
16237            &atn,
16238            AntlrError::ParserError {
16239                line: 1,
16240                column: 0,
16241                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
16242                offending,
16243            },
16244        );
16245        let tree = parser.finish_rule(child, false);
16246
16247        assert_eq!(parser.la(1), TOKEN_EOF);
16248        assert_eq!(
16249            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
16250            "(a z)"
16251        );
16252        assert_eq!(parser.number_of_syntax_errors(), 1);
16253        assert_eq!(
16254            parser.generated_parser_diagnostics,
16255            [ParserDiagnostic {
16256                line: 1,
16257                column: 0,
16258                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
16259                offending,
16260            }]
16261        );
16262        parser.exit_rule();
16263    }
16264
16265    #[test]
16266    fn generated_rule_recovery_forces_progress_after_repeated_error_state() {
16267        let atn = nested_nullable_context_atn();
16268        let mut parser = mini_parser(vec![
16269            TestToken::new(1).with_text("x"),
16270            TestToken::eof("parser-test", 1, 1, 1),
16271        ]);
16272        parser.rule_context_stack = vec![
16273            RuleContextFrame {
16274                rule_index: 0,
16275                invoking_state: 0,
16276            },
16277            RuleContextFrame {
16278                rule_index: 1,
16279                invoking_state: 1,
16280            },
16281            RuleContextFrame {
16282                rule_index: 2,
16283                invoking_state: 2,
16284            },
16285        ];
16286        parser.set_state(20);
16287        let mut context = ParserRuleContext::new(2, 2);
16288
16289        parser.recover_generated_rule(
16290            &mut context,
16291            &atn,
16292            AntlrError::NoViableAlternative {
16293                input: "'x'".to_owned(),
16294            },
16295        );
16296        assert_eq!(parser.input.index(), 0);
16297
16298        parser.set_state(21);
16299        parser.recover_generated_rule(
16300            &mut context,
16301            &atn,
16302            AntlrError::NoViableAlternative {
16303                input: "'x'".to_owned(),
16304            },
16305        );
16306        assert_eq!(parser.input.index(), 0);
16307        assert_eq!(
16308            parser.generated_recovery_error_states,
16309            BTreeSet::from([20, 21])
16310        );
16311
16312        parser.set_state(20);
16313        parser.recover_generated_rule(
16314            &mut context,
16315            &atn,
16316            AntlrError::NoViableAlternative {
16317                input: "'x'".to_owned(),
16318            },
16319        );
16320
16321        assert_eq!(parser.input.index(), 1);
16322        assert_eq!(parser.la(1), TOKEN_EOF);
16323        assert!(context.has_matched_child());
16324        assert_eq!(parser.generated_recovery_error_states, BTreeSet::from([20]));
16325
16326        parser.match_eof().expect("EOF should match");
16327        assert_eq!(parser.generated_recovery_error_index, None);
16328        assert!(parser.generated_recovery_error_states.is_empty());
16329    }
16330
16331    #[test]
16332    fn greedy_ll1_alt_handles_nullable_loop_exit() {
16333        let mut body_symbols = TokenBitSet::default();
16334        body_symbols.insert(1);
16335        let entry = DecisionLookahead {
16336            transitions: vec![
16337                TransitionLookSet {
16338                    symbols: body_symbols,
16339                    nullable: false,
16340                },
16341                TransitionLookSet {
16342                    symbols: TokenBitSet::default(),
16343                    nullable: true,
16344                },
16345            ],
16346        };
16347
16348        assert_eq!(ll1_unique_alt(&entry, 2), None);
16349        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
16350        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
16351        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
16352    }
16353
16354    #[test]
16355    fn ordinary_repetition_builds_tree_in_input_order() {
16356        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16357            let mut parser = mini_parser(repeated_x_tokens(3));
16358            let tree = parser
16359                .parse_atn_rule(&atn, 0)
16360                .expect("ordinary repetition should parse");
16361
16362            let root = parser
16363                .node(tree)
16364                .as_rule()
16365                .expect("entry result should be a rule");
16366            let body_rules = root.child_rules(1).collect::<Vec<_>>();
16367            assert_eq!(root.text(), "xxx<EOF>");
16368            assert_eq!(body_rules.len(), 3);
16369            assert_eq!(
16370                body_rules
16371                    .iter()
16372                    .map(|rule| rule.start_id().expect("body start").index())
16373                    .collect::<Vec<_>>(),
16374                [0, 1, 2]
16375            );
16376            assert_eq!(
16377                body_rules
16378                    .iter()
16379                    .map(|rule| rule.stop_id().expect("body stop").index())
16380                    .collect::<Vec<_>>(),
16381                [0, 1, 2]
16382            );
16383            assert_eq!(parser.number_of_syntax_errors(), 0);
16384        }
16385    }
16386
16387    #[test]
16388    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
16389        const DEPTH: usize = 20_000;
16390
16391        std::thread::Builder::new()
16392            .name("deferred-rule-materialization".to_owned())
16393            .stack_size(256 * 1024)
16394            .spawn(|| {
16395                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16396                let mut root = FastDeferredNodeId::EMPTY;
16397                for depth in 0..DEPTH {
16398                    root = parser
16399                        .recognition_arena
16400                        .deferred_rule_node(FastDeferredRule {
16401                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
16402                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
16403                            start_index: 0,
16404                            stop_index: None,
16405                            deferred_children: root,
16406                            children: NodeSeqId::EMPTY,
16407                        });
16408                }
16409
16410                let (mut children, alt_number) =
16411                    parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
16412                assert_eq!(alt_number, 0);
16413                for expected_rule in (0..DEPTH).rev() {
16414                    let mut nodes = parser.recognition_arena.iter(children);
16415                    let node = nodes.next().expect("nested rule node");
16416                    assert!(nodes.next().is_none(), "each rule has one child");
16417                    let ArenaRecognizedNode::Rule {
16418                        rule_index,
16419                        children: nested,
16420                        ..
16421                    } = parser.recognition_arena.node(node)
16422                    else {
16423                        panic!("expected nested rule");
16424                    };
16425                    assert_eq!(rule_index as usize, expected_rule);
16426                    children = nested;
16427                }
16428                assert!(children.is_empty());
16429            })
16430            .expect("small-stack thread should start")
16431            .join()
16432            .expect("deferred rules should materialize without recursion");
16433    }
16434
16435    #[test]
16436    fn deferred_alternatives_preserve_left_recursive_contexts() {
16437        let mut parser = mini_parser(vec![
16438            TestToken::new(1).with_text("1"),
16439            TestToken::new(2).with_text("+"),
16440            TestToken::new(1).with_text("2"),
16441            TestToken::eof("parser-test", 3, 1, 3),
16442        ]);
16443        let base = parser.arena_token_node(0, false);
16444        let operator = parser.arena_token_node(1, false);
16445        let right = parser.arena_token_node(2, false);
16446
16447        let base = parser.recognition_arena.prepend(NodeSeqId::EMPTY, base);
16448        let base = parser.recognition_arena.deferred_fragment(base);
16449        let operator = parser.recognition_arena.prepend(NodeSeqId::EMPTY, operator);
16450        let operator = parser.recognition_arena.deferred_fragment(operator);
16451        let right = parser.recognition_arena.prepend(NodeSeqId::EMPTY, right);
16452        let right = parser.recognition_arena.deferred_fragment(right);
16453        let base_alt = parser.recognition_arena.deferred_alternative(1);
16454        let boundary = parser.recognition_arena.deferred_left_recursive_boundary(0);
16455        let operator_alt = parser.recognition_arena.deferred_alternative(6);
16456
16457        let mut deferred = FastDeferredNodeId::EMPTY;
16458        for fragment in [base_alt, base, boundary, operator_alt, operator, right] {
16459            deferred = parser
16460                .recognition_arena
16461                .concat_deferred_nodes(deferred, fragment);
16462        }
16463        let (nodes, root_alt_number) =
16464            parser.materialize_fast_deferred_nodes(deferred, NodeSeqId::EMPTY);
16465        let nodes = parser
16466            .recognition_arena
16467            .fold_left_recursive_boundaries(nodes);
16468
16469        let mut root = ParserRuleContext::new(0, -1);
16470        root.set_context_alt_number(root_alt_number);
16471        let mut cursor = nodes;
16472        while let Some(link) = parser.recognition_arena.link(cursor) {
16473            let child = parser
16474                .arena_recognized_node_tree(link.head, false, true)
16475                .expect("materialized child should become a public tree");
16476            parser.tree.add_child(&mut root, child);
16477            cursor = link.tail;
16478        }
16479        let tree = parser.rule_node(root);
16480        let contexts = parser
16481            .node(tree)
16482            .descendants()
16483            .filter_map(Node::as_rule)
16484            .map(|rule| {
16485                (
16486                    rule.rule_index(),
16487                    rule.alt_number(),
16488                    rule.context_alt_number(),
16489                    rule.text(),
16490                )
16491            })
16492            .collect::<Vec<_>>();
16493
16494        insta::assert_debug_snapshot!(
16495            "deferred_alternatives_preserve_left_recursive_contexts",
16496            contexts
16497        );
16498    }
16499
16500    #[test]
16501    fn fast_recognizer_preserves_labeled_left_recursive_operator_context() {
16502        let atn = labeled_left_recursive_operator_atn();
16503        let mut parser = mini_parser(vec![
16504            TestToken::new(1).with_text("a"),
16505            TestToken::new(3).with_text("+"),
16506            TestToken::new(1).with_text("b"),
16507            TestToken::eof("parser-test", 3, 1, 3),
16508        ]);
16509
16510        let (tree, _) = parser
16511            .parse_atn_rule_with_runtime_options(
16512                &atn,
16513                0,
16514                ParserRuntimeOptions {
16515                    track_context_alt_numbers: true,
16516                    ..ParserRuntimeOptions::default()
16517                },
16518            )
16519            .expect("labeled left-recursive addition should parse");
16520        let contexts = parser
16521            .node(tree)
16522            .descendants()
16523            .filter_map(Node::as_rule)
16524            .map(|rule| {
16525                let operator = rule
16526                    .children()
16527                    .next()
16528                    .and_then(Node::as_rule)
16529                    .is_some_and(|child| child.rule_index() == rule.rule_index());
16530                (operator, rule.context_alt_number(), rule.text())
16531            })
16532            .collect::<Vec<_>>();
16533
16534        insta::assert_debug_snapshot!(
16535            "fast_recognizer_preserves_labeled_left_recursive_operator_context",
16536            contexts
16537        );
16538        assert!(!parser.recognition_arena.deferred_nodes.is_empty());
16539        assert_eq!(parser.number_of_syntax_errors(), 0);
16540    }
16541
16542    #[test]
16543    fn deeply_nested_rule_calls_grow_the_stack() {
16544        const DEPTH: usize = 4_096;
16545        const STACK_SIZE: usize = 256 * 1024;
16546        let atn = nested_rule_chain_atn(DEPTH);
16547        std::thread::Builder::new()
16548            .name("nested-adaptive-set-rules".to_owned())
16549            .stack_size(STACK_SIZE)
16550            .spawn(move || {
16551                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16552                parser.set_build_parse_trees(false);
16553                // This test isolates recognizer depth from the separately
16554                // cached FIRST-set metadata walk.
16555                parser.fast_first_set_prefilter = false;
16556                parser
16557                    .parse_atn_rule(&atn, 0)
16558                    .expect("nested rule chain should grow the native stack");
16559                assert_eq!(parser.input.index(), 1);
16560            })
16561            .expect("small-stack thread should start")
16562            .join()
16563            .expect("nested rule chain should not overflow its stack");
16564    }
16565
16566    #[test]
16567    fn deeply_nested_branching_rules_grow_the_stack() {
16568        const DEPTH: usize = 4_096;
16569        const STACK_SIZE: usize = 256 * 1024;
16570        let atn = nested_rule_graph_atn(DEPTH, true, false);
16571        std::thread::Builder::new()
16572            .name("nested-branching-rules".to_owned())
16573            .stack_size(STACK_SIZE)
16574            .spawn(move || {
16575                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16576                parser.set_build_parse_trees(false);
16577                parser
16578                    .parse_atn_rule(&atn, 0)
16579                    .expect("branching rule chain should grow the native stack");
16580                assert_eq!(parser.input.index(), 1);
16581            })
16582            .expect("small-stack thread should start")
16583            .join()
16584            .expect("branching rule chain should not overflow its stack");
16585    }
16586
16587    #[test]
16588    fn deeply_nested_rule_follows_grow_the_stack() {
16589        const DEPTH: usize = 4_096;
16590        const STACK_SIZE: usize = 256 * 1024;
16591        let atn = nested_rule_graph_atn(DEPTH, false, true);
16592        std::thread::Builder::new()
16593            .name("nested-rule-follows".to_owned())
16594            .stack_size(STACK_SIZE)
16595            .spawn(move || {
16596                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
16597                parser.set_build_parse_trees(false);
16598                parser.fast_first_set_prefilter = false;
16599                parser
16600                    .parse_atn_rule(&atn, 0)
16601                    .expect("rule follow chain should grow the native stack");
16602                assert_eq!(parser.input.index(), DEPTH);
16603            })
16604            .expect("small-stack thread should start")
16605            .join()
16606            .expect("nested rule follow chain should not overflow its stack");
16607    }
16608
16609    #[test]
16610    fn deeply_nested_recovery_grows_the_stack() {
16611        const DEPTH: usize = 4_096;
16612        const STACK_SIZE: usize = 256 * 1024;
16613        let atn = nested_rule_chain_atn(DEPTH);
16614        std::thread::Builder::new()
16615            .name("nested-rule-recovery".to_owned())
16616            .stack_size(STACK_SIZE)
16617            .spawn(move || {
16618                let mut parser = mini_parser(vec![
16619                    TestToken::new(2).with_text("z"),
16620                    TestToken::new(1).with_text("x"),
16621                    TestToken::eof("parser-test", 2, 1, 2),
16622                ]);
16623                parser.set_build_parse_trees(false);
16624                parser.fast_first_set_prefilter = false;
16625                parser
16626                    .parse_atn_rule(&atn, 0)
16627                    .expect("nested recovery should grow the native stack");
16628                assert_eq!(parser.input.index(), 2);
16629                assert_eq!(parser.number_of_syntax_errors(), 1);
16630            })
16631            .expect("small-stack thread should start")
16632            .join()
16633            .expect("nested rule recovery should not overflow its stack");
16634    }
16635
16636    #[test]
16637    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
16638        const REPETITIONS: usize = 64;
16639
16640        let atn = ambiguous_ordinary_star_loop_atn();
16641        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16642        let tree = parser
16643            .parse_atn_rule(&atn, 0)
16644            .expect("ambiguous ordinary repetition should parse");
16645
16646        let root = parser
16647            .node(tree)
16648            .as_rule()
16649            .expect("entry result should be a rule");
16650        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
16651        assert_eq!(parser.input.index(), REPETITIONS);
16652        assert!(
16653            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
16654            "equivalent segmentations should keep deferred storage linear"
16655        );
16656        assert_eq!(parser.number_of_syntax_errors(), 0);
16657    }
16658
16659    #[test]
16660    fn long_ordinary_repetition_does_not_consume_native_stack() {
16661        const REPETITIONS: usize = 20_000;
16662
16663        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16664            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16665            parser.set_build_parse_trees(false);
16666            parser
16667                .parse_atn_rule(&atn, 0)
16668                .expect("long ordinary repetition should parse");
16669
16670            assert_eq!(parser.input.index(), REPETITIONS);
16671            assert_eq!(parser.number_of_syntax_errors(), 0);
16672        }
16673    }
16674
16675    #[test]
16676    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
16677        const REPETITIONS: usize = 2_000;
16678        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
16679
16680        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16681            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16682            let tree = parser
16683                .parse_atn_rule(&atn, 0)
16684                .expect("long rule repetition should parse");
16685
16686            let root = parser
16687                .node(tree)
16688                .as_rule()
16689                .expect("entry result should be a rule");
16690            assert_eq!(root.text(), expected_text);
16691            assert_eq!(root.child_rules(1).count(), REPETITIONS);
16692            let first_body = root.child_rules(1).next().expect("first body rule");
16693            let last_body = root.child_rules(1).next_back().expect("last body rule");
16694            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
16695            assert_eq!(
16696                last_body.stop_id().expect("last body stop").index(),
16697                REPETITIONS - 1
16698            );
16699
16700            let stats = parser.recognition_arena_stats();
16701            assert_eq!(
16702                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16703                (REPETITIONS, REPETITIONS, 0)
16704            );
16705            assert_eq!(
16706                (stats.total_links, stats.live_links, stats.dead_links),
16707                (REPETITIONS, REPETITIONS, 0)
16708            );
16709            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
16710            assert_eq!(
16711                parser.recognition_arena.deferred_nodes.len(),
16712                REPETITIONS * 2 - 1
16713            );
16714            assert_eq!(parser.number_of_syntax_errors(), 0);
16715        }
16716    }
16717
16718    #[test]
16719    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
16720        let key = |state_number| FastRecognizeKey {
16721            state_number,
16722            stop_state: 10,
16723            index: state_number,
16724            rule_start_index: 0,
16725            decision_start_index: None,
16726            precedence: 0,
16727            recovery_symbols_id: 0,
16728            recovery_state: None,
16729        };
16730
16731        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16732        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
16733            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
16734        }
16735        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
16736        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
16737
16738        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16739        let repeated = key(1);
16740        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
16741            assert!(promote.clean_memo_enabled_for_key(&repeated));
16742        }
16743        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
16744
16745        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
16746            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
16747        }
16748        assert!(sparse.clean_memo_enabled_for_key(&repeated));
16749        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
16750        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
16751            assert!(sparse.clean_memo_enabled_for_key(&repeated));
16752        }
16753        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
16754    }
16755
16756    #[test]
16757    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
16758        assert_eq!(
16759            fast_recognize_memo_capacity(0),
16760            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16761        );
16762        assert_eq!(
16763            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
16764            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16765        );
16766        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
16767        assert_eq!(
16768            fast_recognize_memo_capacity(usize::MAX),
16769            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
16770        );
16771    }
16772
16773    #[test]
16774    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
16775        let mut scratch = FastRecognizeTopScratch::default();
16776        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16777        let retained_capacity = scratch.memo.capacity();
16778        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16779        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16780
16781        let larger_capacity = retained_capacity + 1;
16782        scratch.prepare(larger_capacity);
16783        let grown_capacity = scratch.memo.capacity();
16784        assert!(grown_capacity >= larger_capacity);
16785        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16786
16787        scratch.memo.insert(
16788            FastRecognizeKey {
16789                state_number: 0,
16790                stop_state: 0,
16791                index: 0,
16792                rule_start_index: 0,
16793                decision_start_index: None,
16794                precedence: 0,
16795                recovery_symbols_id: 0,
16796                recovery_state: None,
16797            },
16798            Rc::from([FastRecognizeOutcome {
16799                index: 0,
16800                consumed_eof: false,
16801                diagnostics: DiagnosticSeqId::EMPTY,
16802                deferred_nodes: FastDeferredNodeId::EMPTY,
16803                nodes: NodeSeqId::EMPTY,
16804            }]),
16805        );
16806        scratch.release_oversized_memo();
16807        assert!(scratch.memo.is_empty());
16808        assert_eq!(scratch.memo.capacity(), grown_capacity);
16809
16810        scratch
16811            .memo
16812            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
16813        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16814
16815        scratch.release_oversized_memo();
16816        assert!(scratch.memo.is_empty());
16817        assert_eq!(scratch.memo.capacity(), 0);
16818    }
16819
16820    #[test]
16821    fn clean_empty_multi_alt_outcomes_are_memoized() {
16822        let mut atn = ParserAtnBuilder::new(2);
16823        assert_eq!(
16824            atn.add_state(AtnStateKind::RuleStart, Some(0))
16825                .expect("state")
16826                .index(),
16827            0
16828        );
16829        assert_eq!(
16830            atn.add_state(AtnStateKind::BlockStart, Some(0))
16831                .expect("state")
16832                .index(),
16833            1
16834        );
16835        assert_eq!(
16836            atn.add_state(AtnStateKind::RuleStop, Some(0))
16837                .expect("state")
16838                .index(),
16839            2
16840        );
16841        atn.set_rule_to_start_state(vec![0])
16842            .expect("rule start states");
16843        atn.set_rule_to_stop_state(vec![2])
16844            .expect("rule stop states");
16845        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16846            .expect("transition");
16847        atn.add_transition(
16848            1,
16849            ParserTransitionSpec::Atom {
16850                target: 2,
16851                label: 1,
16852            },
16853        )
16854        .expect("transition");
16855        atn.add_transition(
16856            1,
16857            ParserTransitionSpec::Atom {
16858                target: 2,
16859                label: 2,
16860            },
16861        )
16862        .expect("transition");
16863        let atn = finish_atn(atn);
16864
16865        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16866        parser.fast_recovery_enabled = false;
16867        let mut visiting = FxHashSet::default();
16868        let mut memo = FxHashMap::default();
16869        let mut expected = ExpectedTokens::default();
16870        let outcomes = parser.recognize_state_fast(
16871            &atn,
16872            FastRecognizeRequest {
16873                state_number: 1,
16874                stop_state: 2,
16875                index: 0,
16876                rule_start_index: 0,
16877                decision_start_index: None,
16878                precedence: 0,
16879                depth: 0,
16880                recovery_symbols: parser.empty_recovery_symbols(),
16881                recovery_state: None,
16882            },
16883            FastRecognizeScratch {
16884                predicate_context: None,
16885                visiting: &mut visiting,
16886                memo: &mut memo,
16887                expected: &mut expected,
16888                native_depth: 0,
16889            },
16890        );
16891
16892        assert!(outcomes.is_empty());
16893        assert_eq!(memo.len(), 1);
16894        assert!(memo.values().next().expect("memo entry").is_empty());
16895
16896        parser.clean_memo_mode = CleanMemoMode::Sparse;
16897        visiting.clear();
16898        memo.clear();
16899        expected = ExpectedTokens::default();
16900        let sparse_outcomes = parser.recognize_state_fast(
16901            &atn,
16902            FastRecognizeRequest {
16903                state_number: 1,
16904                stop_state: 2,
16905                index: 0,
16906                rule_start_index: 0,
16907                decision_start_index: None,
16908                precedence: 0,
16909                depth: 0,
16910                recovery_symbols: parser.empty_recovery_symbols(),
16911                recovery_state: None,
16912            },
16913            FastRecognizeScratch {
16914                predicate_context: None,
16915                visiting: &mut visiting,
16916                memo: &mut memo,
16917                expected: &mut expected,
16918                native_depth: 0,
16919            },
16920        );
16921
16922        assert!(sparse_outcomes.is_empty());
16923        assert!(memo.is_empty());
16924    }
16925
16926    #[test]
16927    fn wildcard_matches_non_eof_only() {
16928        let mut parser = mini_parser(vec![
16929            TestToken::new(1).with_text("x"),
16930            TestToken::eof("parser-test", 1, 1, 1),
16931        ]);
16932        let matched = parser.match_wildcard().expect("wildcard");
16933        assert_eq!(parser.node(matched).text(), "x");
16934        assert!(parser.match_wildcard().is_err());
16935    }
16936
16937    #[test]
16938    fn add_parse_child_records_match_even_without_tree_building() {
16939        // `sync_decision`'s "is the current context empty" flag must reflect real
16940        // matches, not parse-tree children: when `build_parse_trees(false)`,
16941        // `children` stays empty but `has_matched_child` must still flip so nested
16942        // recovery does not wrongly suppress single-token deletion.
16943        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16944        let token = TestToken::new(1).with_text("x");
16945
16946        parser.set_build_parse_trees(false);
16947        let mut ctx = ParserRuleContext::new(0, 0);
16948        assert!(!ctx.has_matched_child());
16949        let child = parser.terminal_tree(token.id);
16950        parser.add_parse_child(&mut ctx, child);
16951        // Tree building is off, so no child is stored...
16952        assert_eq!(ctx.child_count(), 0);
16953        assert_eq!(parser.parse_tree_storage().node_count(), 0);
16954        // ...but the match is recorded, so the context is no longer "empty".
16955        assert!(ctx.has_matched_child());
16956
16957        // With tree building on, the child is stored and the match is recorded.
16958        parser.set_build_parse_trees(true);
16959        let mut ctx = ParserRuleContext::new(0, 0);
16960        let child = parser.terminal_tree(token.id);
16961        parser.add_parse_child(&mut ctx, child);
16962        assert_eq!(ctx.child_count(), 1);
16963        assert!(ctx.has_matched_child());
16964    }
16965
16966    #[test]
16967    fn disabled_tree_building_does_not_grow_flat_storage() {
16968        let mut parser = mini_parser(vec![
16969            TestToken::new(1).with_text("x"),
16970            TestToken::new(1).with_text("y"),
16971            TestToken::eof("parser-test", 2, 1, 2),
16972        ]);
16973        parser.set_build_parse_trees(false);
16974        let mut context = ParserRuleContext::new(0, -1);
16975
16976        for _ in 0..2 {
16977            let child = parser.match_token(1).expect("token should match");
16978            parser.add_parse_child(&mut context, child);
16979        }
16980        let current = parser.input.lt_id(1).expect("EOF token");
16981        let error = parser.error_tree(current);
16982        parser.add_parse_child(&mut context, error);
16983        let root = parser.rule_node(context);
16984
16985        assert_eq!(
16986            parser.parse_tree_storage().stats(),
16987            ParseTreeStats::default()
16988        );
16989        assert!(
16990            parser
16991                .parse_tree_storage()
16992                .node(parser.token_store(), root)
16993                .is_none(),
16994            "the no-tree sentinel must not resolve to stored data"
16995        );
16996    }
16997
16998    #[test]
16999    fn disabled_tree_building_skips_recognition_rule_node_storage() {
17000        let atn = ordinary_star_loop_atn();
17001        let mut parser = mini_parser(repeated_x_tokens(3));
17002        parser.set_build_parse_trees(false);
17003
17004        parser
17005            .parse_atn_rule(&atn, 0)
17006            .expect("ordinary repetition should parse without a tree");
17007
17008        assert_eq!(parser.input.index(), 3);
17009        assert!(parser.recognition_arena.nodes.is_empty());
17010        assert!(parser.recognition_arena.seq_links.is_empty());
17011        assert!(parser.recognition_arena.deferred_nodes.is_empty());
17012        assert!(parser.recognition_arena.deferred_rules.is_empty());
17013        assert!(!parser.fast_token_nodes_enabled);
17014        assert!(parser.fast_recognize_scratch.memo.is_empty());
17015    }
17016
17017    #[test]
17018    fn parser_interprets_simple_atn_rule() {
17019        let atn = token_then_eof_atn();
17020        let mut parser = mini_parser(vec![
17021            TestToken::new(1).with_text("x"),
17022            TestToken::eof("parser-test", 1, 1, 1),
17023        ]);
17024
17025        let tree = parser
17026            .parse_atn_rule(&atn, 0)
17027            .expect("artificial parser rule should parse");
17028        assert_eq!(parser.node(tree).text(), "x<EOF>");
17029        assert_eq!(parser.number_of_syntax_errors(), 0);
17030        assert_eq!(
17031            parser
17032                .node(tree)
17033                .first_rule_stop(0)
17034                .expect("rule should stop at EOF")
17035                .token_type(),
17036            TOKEN_EOF
17037        );
17038
17039        let mut parser = mini_parser(vec![
17040            TestToken::new(1).with_text("x"),
17041            TestToken::eof("parser-test", 1, 1, 1),
17042        ]);
17043        let (tree, actions) = parser
17044            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17045            .expect("runtime-option parser rule should parse");
17046        assert!(actions.is_empty());
17047        assert_eq!(
17048            parser
17049                .node(tree)
17050                .first_rule_stop(0)
17051                .expect("rule should stop at EOF")
17052                .token_type(),
17053            TOKEN_EOF
17054        );
17055    }
17056
17057    #[test]
17058    fn runtime_options_default_ignores_noop_action_transitions() {
17059        let atn = noop_action_then_token_then_eof_atn();
17060        let mut parser = mini_parser(vec![
17061            TestToken::new(1).with_text("x"),
17062            TestToken::eof("parser-test", 1, 1, 1),
17063        ]);
17064
17065        let (tree, actions) = parser
17066            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17067            .expect("no-op parser action should not force action replay");
17068
17069        assert_eq!(parser.node(tree).text(), "x<EOF>");
17070        assert!(
17071            actions.is_empty(),
17072            "action_index=None transitions are ANTLR metadata, not replay actions"
17073        );
17074        assert_eq!(parser.number_of_syntax_errors(), 0);
17075    }
17076
17077    #[test]
17078    fn parser_exposes_buffered_token_stream_after_parse() {
17079        let atn = token_then_eof_atn();
17080        let mut parser = mini_parser(vec![
17081            TestToken::new(1).with_text("x"),
17082            TestToken::eof("parser-test", 1, 1, 1),
17083        ]);
17084
17085        let tree = parser
17086            .parse_atn_rule(&atn, 0)
17087            .expect("artificial parser rule should parse");
17088        assert_eq!(parser.node(tree).text(), "x<EOF>");
17089
17090        let stream = parser.token_stream();
17091        let source_index_after_parse = stream.token_source().index;
17092        let buffered = stream.tokens().collect::<Vec<_>>();
17093        assert_eq!(buffered.len(), 2);
17094        assert_eq!(buffered[0].text(), Some("x"));
17095        assert_eq!(buffered[0].token_id().index(), 0);
17096        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
17097        assert_eq!(stream.token_source().index, source_index_after_parse);
17098        drop(buffered);
17099
17100        let stream = parser.into_token_stream();
17101        assert_eq!(stream.token_source().index, source_index_after_parse);
17102        assert_eq!(
17103            stream.tokens().next().expect("first token").text(),
17104            Some("x")
17105        );
17106        assert_eq!(
17107            stream.tokens().nth(1).expect("EOF token").token_type(),
17108            TOKEN_EOF
17109        );
17110    }
17111
17112    #[test]
17113    fn parsed_file_exposes_all_buffered_tokens() {
17114        let atn = token_then_eof_atn();
17115        let mut parser = mini_parser(vec![
17116            TestToken::new(99)
17117                .with_text(" comment")
17118                .with_channel(HIDDEN_CHANNEL),
17119            TestToken::new(1).with_text("x"),
17120            TestToken::eof("parser-test", 9, 1, 9),
17121        ]);
17122
17123        let tree = parser
17124            .parse_atn_rule(&atn, 0)
17125            .expect("artificial parser rule should parse");
17126        let parsed = parser.into_parsed_file(tree);
17127
17128        // Snapshot the full buffered stream — hidden-channel comment, default-channel token, EOF —
17129        // as (type, channel, text) triples; contents make the count self-evident.
17130        insta::assert_debug_snapshot!(
17131            "parsed_file_exposes_all_buffered_tokens",
17132            parsed
17133                .tokens()
17134                .iter()
17135                .map(|token| (token.token_type(), token.channel(), token.text()))
17136                .collect::<Vec<_>>()
17137        );
17138        assert_eq!(parsed.tokens().into_iter().count(), 3);
17139    }
17140
17141    #[test]
17142    fn parser_syntax_error_count_tracks_interpreted_recovery() {
17143        let atn = token_then_eof_atn();
17144        let mut parser = mini_parser(vec![
17145            TestToken::new(1).with_text("x"),
17146            TestToken::new(2).with_text("y"),
17147            TestToken::eof("parser-test", 2, 1, 2),
17148        ]);
17149
17150        let tree = parser
17151            .parse_atn_rule(&atn, 0)
17152            .expect("invalid token should recover into an error node");
17153
17154        assert_eq!(parser.number_of_syntax_errors(), 1);
17155        assert_eq!(
17156            parser
17157                .node(tree)
17158                .first_error_token()
17159                .expect("recovery should embed an error token")
17160                .text(),
17161            Some("y")
17162        );
17163    }
17164
17165    #[test]
17166    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
17167        let atn = token_then_eof_atn();
17168        let mut parser = mini_parser(vec![
17169            TestToken::new(2).with_text("y"),
17170            TestToken::eof("parser-test", 1, 1, 1),
17171        ]);
17172
17173        let error = parser
17174            .parse_atn_rule(&atn, 0)
17175            .expect_err("start-rule mismatch should remain a parser error");
17176
17177        assert_eq!(parser.number_of_syntax_errors(), 1);
17178        assert!(matches!(error, AntlrError::ParserError { .. }));
17179    }
17180
17181    #[test]
17182    fn adaptive_direct_rule_uses_simulator_decision() {
17183        let atn = two_alt_decision_atn();
17184        let mut simulator = ParserAtnSimulator::new(&atn);
17185        let mut parser = mini_parser(vec![
17186            TestToken::new(2).with_text("y"),
17187            TestToken::eof("parser-test", 1, 1, 1),
17188        ]);
17189
17190        let tree = parser
17191            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
17192            .expect("direct adaptive rule should parse");
17193
17194        assert_eq!(parser.node(tree).text(), "y");
17195        assert_eq!(parser.input.index(), 1);
17196    }
17197
17198    #[test]
17199    fn adaptive_direct_rule_restores_input_on_fallback() {
17200        let atn = predicate_after_token_atn();
17201        let mut simulator = ParserAtnSimulator::new(&atn);
17202        let mut parser = mini_parser(vec![
17203            TestToken::new(1).with_text("x"),
17204            TestToken::new(2).with_text("y"),
17205            TestToken::eof("parser-test", 2, 1, 2),
17206        ]);
17207
17208        let tree = parser
17209            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
17210            .expect("fallback recognizer should parse");
17211
17212        assert_eq!(parser.node(tree).text(), "xy");
17213        assert_eq!(parser.input.index(), 2);
17214        let stats = parser.parse_tree_storage().stats();
17215        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
17216        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
17217        assert_eq!(stats.scratch_links, 0);
17218    }
17219
17220    #[test]
17221    fn unknown_predicate_policy_defaults_to_assume_true() {
17222        let atn = predicate_after_token_atn();
17223        let mut parser = mini_parser(vec![
17224            TestToken::new(1).with_text("x"),
17225            TestToken::new(2).with_text("y"),
17226            TestToken::eof("parser-test", 2, 1, 2),
17227        ]);
17228
17229        let (tree, _) = parser
17230            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17231            .expect("unknown predicate should pass under the default policy");
17232
17233        assert_eq!(parser.node(tree).text(), "xy");
17234        assert_eq!(parser.number_of_syntax_errors(), 0);
17235    }
17236
17237    #[test]
17238    fn private_context_alt_tracking_keeps_fast_predicate_recognition() {
17239        let atn = predicate_gated_same_lookahead_atn([0, 1]);
17240        let mut parser = mini_parser(vec![
17241            TestToken::new(1).with_text("x"),
17242            TestToken::eof("parser-test", 1, 1, 1),
17243        ]);
17244
17245        let (tree, _) = parser
17246            .parse_atn_rule_with_runtime_options(
17247                &atn,
17248                0,
17249                ParserRuntimeOptions {
17250                    predicates: &[
17251                        (0, 0, ParserPredicate::False),
17252                        (0, 1, ParserPredicate::True),
17253                    ],
17254                    track_context_alt_numbers: true,
17255                    ..ParserRuntimeOptions::default()
17256                },
17257            )
17258            .expect("the second predicate-gated alternative should match");
17259
17260        let root = parser.node(tree).as_rule().expect("entry result is a rule");
17261        insta::assert_debug_snapshot!(
17262            "private_context_alt_tracking_keeps_fast_predicate_recognition",
17263            (root.alt_number(), root.context_alt_number(), root.text())
17264        );
17265        assert_eq!(parser.number_of_syntax_errors(), 0);
17266        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
17267        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
17268    }
17269
17270    #[test]
17271    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
17272        // A generated parent may record an unknown-predicate coordinate, then
17273        // descend into an interpreted child. The child's interpreter entry must
17274        // not wipe the parent's recorded hit before the top-level surfaces it.
17275        let atn = token_then_eof_atn();
17276        let mut parser = mini_parser(vec![
17277            TestToken::new(1).with_text("x"),
17278            TestToken::eof("parser-test", 1, 1, 1),
17279        ]);
17280
17281        // Simulate the parent having recorded a fail-loud coordinate.
17282        parser.unknown_predicate_hits.push((7, 3));
17283
17284        // Run an interpreted child parse that records no coordinate of its own.
17285        parser
17286            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17287            .expect("child rule parses");
17288
17289        // The parent's coordinate must still be present for the top-level entry.
17290        let error = parser
17291            .take_unknown_semantic_error()
17292            .expect("parent's recorded coordinate must survive the nested interpreted parse");
17293        let AntlrError::Unsupported(message) = error else {
17294            panic!("expected AntlrError::Unsupported, got {error:?}");
17295        };
17296        assert!(message.contains("pred_index=3"), "message: {message}");
17297    }
17298
17299    #[test]
17300    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
17301        let atn = predicate_after_token_atn();
17302        let mut parser = mini_parser(vec![
17303            TestToken::new(1).with_text("x"),
17304            TestToken::new(2).with_text("y"),
17305            TestToken::eof("parser-test", 2, 1, 2),
17306        ]);
17307
17308        let result = parser.parse_atn_rule_with_runtime_options(
17309            &atn,
17310            0,
17311            ParserRuntimeOptions {
17312                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17313                ..ParserRuntimeOptions::default()
17314            },
17315        );
17316
17317        assert!(
17318            result.is_err(),
17319            "the only path is predicate-guarded, so assume-false must fail the parse"
17320        );
17321    }
17322
17323    #[test]
17324    fn predicate_failure_message_keeps_semantic_recovery_path() {
17325        let atn = predicate_after_token_atn();
17326        let mut parser = mini_parser(vec![
17327            TestToken::new(1).with_text("x"),
17328            TestToken::new(2).with_text("y"),
17329            TestToken::eof("parser-test", 2, 1, 2),
17330        ]);
17331
17332        let (tree, _) = parser
17333            .parse_atn_rule_with_runtime_options(
17334                &atn,
17335                0,
17336                ParserRuntimeOptions {
17337                    predicates: &[(
17338                        0,
17339                        0,
17340                        ParserPredicate::FalseWithMessage {
17341                            message: "predicate rejected input",
17342                        },
17343                    )],
17344                    ..ParserRuntimeOptions::default()
17345                },
17346            )
17347            .expect("failure-message predicates recover through the semantic interpreter");
17348
17349        assert_eq!(parser.node(tree).text(), "xy");
17350        assert_eq!(parser.number_of_syntax_errors(), 1);
17351        assert!(
17352            parser.fast_predicate_cache.is_empty(),
17353            "failure-message predicates need the semantic interpreter's recovery outcome"
17354        );
17355    }
17356
17357    #[test]
17358    fn unknown_predicate_policy_error_names_the_coordinate() {
17359        let atn = predicate_after_token_atn();
17360        let mut parser = mini_parser(vec![
17361            TestToken::new(1).with_text("x"),
17362            TestToken::new(2).with_text("y"),
17363            TestToken::eof("parser-test", 2, 1, 2),
17364        ]);
17365
17366        let error = parser
17367            .parse_atn_rule_with_runtime_options(
17368                &atn,
17369                0,
17370                ParserRuntimeOptions {
17371                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17372                    ..ParserRuntimeOptions::default()
17373                },
17374            )
17375            .expect_err("evaluating an unknown predicate under Error policy must fail");
17376
17377        let AntlrError::Unsupported(message) = error else {
17378            panic!("expected AntlrError::Unsupported, got {error:?}");
17379        };
17380        assert!(
17381            message.contains("unsupported semantic predicate"),
17382            "message should name the failure class: {message}"
17383        );
17384        assert!(
17385            message.contains("pred_index=0"),
17386            "message should carry the coordinate: {message}"
17387        );
17388    }
17389
17390    #[test]
17391    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
17392        // A parser reused after a fail-loud parse must not carry the old
17393        // coordinates into a later parse. The fail-loud return keeps the hits
17394        // (so a generated parent can surface a recovered child's coordinate),
17395        // and the next parse's entry stashes/replaces them, so a subsequent
17396        // clean parse surfaces no stale error.
17397        let atn = predicate_after_token_atn();
17398        let mut parser = mini_parser(vec![
17399            TestToken::new(1).with_text("x"),
17400            TestToken::new(2).with_text("y"),
17401            TestToken::eof("parser-test", 2, 1, 2),
17402        ]);
17403
17404        parser
17405            .parse_atn_rule_with_runtime_options(
17406                &atn,
17407                0,
17408                ParserRuntimeOptions {
17409                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17410                    ..ParserRuntimeOptions::default()
17411                },
17412            )
17413            .expect_err("first parse fails loud under the Error policy");
17414
17415        // The failed parse kept its coordinate on the parser (so a generated
17416        // parent could surface a recovered child). A top-level reuse resets the
17417        // hits — generated parsers call `reset_unknown_semantic_hits` at their
17418        // public entry; direct interpreter-API callers do the same.
17419        parser.reset_unknown_semantic_hits();
17420        assert!(
17421            parser.take_unknown_semantic_error().is_none(),
17422            "reset must drop stale unknown-predicate coordinates before a reused parse"
17423        );
17424    }
17425
17426    #[derive(Debug, Default)]
17427    struct RecordingHooks {
17428        predicates: Vec<(usize, usize, usize, Option<String>)>,
17429        actions: Vec<(usize, String, Option<String>)>,
17430        action_trees: Vec<Option<String>>,
17431    }
17432
17433    impl SemanticHooks for RecordingHooks {
17434        fn sempred<S>(
17435            &mut self,
17436            ctx: &mut ParserSemCtx<'_, S>,
17437            rule_index: usize,
17438            pred_index: usize,
17439        ) -> Option<bool>
17440        where
17441            S: TokenSource,
17442        {
17443            self.predicates.push((
17444                ctx.input_index(),
17445                rule_index,
17446                pred_index,
17447                ctx.token_text(1)
17448                    .and_then(|token| token.text().map(str::to_owned)),
17449            ));
17450            Some(true)
17451        }
17452
17453        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
17454        where
17455            S: TokenSource,
17456        {
17457            self.actions.push((
17458                action.source_state(),
17459                ctx.action_text(),
17460                ctx.rule_name().map(str::to_owned),
17461            ));
17462            self.action_trees.push(ctx.tree().map(Node::text));
17463            true
17464        }
17465    }
17466
17467    #[derive(Debug, Default)]
17468    struct RejectingPredicateHooks {
17469        predicates: Vec<(usize, usize, usize, Option<String>)>,
17470    }
17471
17472    impl SemanticHooks for RejectingPredicateHooks {
17473        fn sempred<S>(
17474            &mut self,
17475            ctx: &mut ParserSemCtx<'_, S>,
17476            rule_index: usize,
17477            pred_index: usize,
17478        ) -> Option<bool>
17479        where
17480            S: TokenSource,
17481        {
17482            self.predicates.push((
17483                ctx.input_index(),
17484                rule_index,
17485                pred_index,
17486                ctx.token_text(1)
17487                    .and_then(|token| token.text().map(str::to_owned)),
17488            ));
17489            Some(false)
17490        }
17491    }
17492
17493    #[test]
17494    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
17495        let atn = predicate_gated_same_lookahead_atn([0, 0]);
17496        let mut parser = mini_parser_with_hooks(
17497            vec![
17498                TestToken::new(1).with_text("x"),
17499                TestToken::eof("parser-test", 1, 1, 1),
17500            ],
17501            RecordingHooks::default(),
17502        );
17503
17504        let (tree, _) = parser
17505            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17506            .expect("both alternatives share one replay-safe predicate result");
17507
17508        assert_eq!(parser.node(tree).text(), "x<EOF>");
17509        assert_eq!(
17510            parser.semantic_hooks.predicates,
17511            vec![(0, 0, 0, Some("x".to_owned()))]
17512        );
17513        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
17514    }
17515
17516    #[test]
17517    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
17518        let atn = predicate_after_token_atn();
17519        let mut parser = mini_parser_with_hooks(
17520            vec![
17521                TestToken::new(1).with_text("x"),
17522                TestToken::new(2).with_text("y"),
17523                TestToken::eof("parser-test", 2, 1, 2),
17524            ],
17525            RecordingHooks::default(),
17526        );
17527
17528        let (tree, _) = parser
17529            .parse_atn_rule_with_runtime_options(
17530                &atn,
17531                0,
17532                ParserRuntimeOptions {
17533                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17534                    ..ParserRuntimeOptions::default()
17535                },
17536            )
17537            .expect("hook supplies the missing predicate result");
17538
17539        assert_eq!(parser.node(tree).text(), "xy");
17540        assert_eq!(
17541            parser.semantic_hooks.predicates,
17542            vec![(1, 0, 0, Some("y".to_owned()))]
17543        );
17544        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
17545    }
17546
17547    #[test]
17548    fn runtime_options_default_preserves_semantic_hook_predicates() {
17549        let atn = predicate_after_token_atn();
17550        let mut parser = mini_parser_with_hooks(
17551            vec![
17552                TestToken::new(1).with_text("x"),
17553                TestToken::new(2).with_text("y"),
17554                TestToken::eof("parser-test", 2, 1, 2),
17555            ],
17556            RejectingPredicateHooks::default(),
17557        );
17558
17559        let result =
17560            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
17561
17562        assert!(
17563            result.is_err(),
17564            "default runtime options must not bypass semantic hooks for predicate ATNs"
17565        );
17566        assert_eq!(
17567            parser.semantic_hooks.predicates,
17568            vec![(1, 0, 0, Some("y".to_owned()))]
17569        );
17570        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
17571    }
17572
17573    #[test]
17574    fn semantic_hook_handles_committed_parser_action() {
17575        let atn = token_then_eof_atn();
17576        let mut parser = mini_parser_with_hooks(
17577            vec![
17578                TestToken::new(1).with_text("x"),
17579                TestToken::eof("parser-test", 1, 1, 1),
17580            ],
17581            RecordingHooks::default(),
17582        );
17583        let (tree, _) = parser
17584            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17585            .expect("rule parses before action hook is tested");
17586
17587        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17588        assert_eq!(
17589            parser.semantic_hooks.actions,
17590            vec![(42, "x".to_owned(), Some("s".to_owned()))]
17591        );
17592        assert_eq!(
17593            parser.semantic_hooks.action_trees,
17594            [Some("x<EOF>".to_owned())]
17595        );
17596    }
17597
17598    #[test]
17599    fn unhandled_committed_action_fails_loud_under_error_policy() {
17600        // An action offered to the hook that no hook handles (returns false)
17601        // must be recorded and surfaced as `AntlrError::Unsupported` under the
17602        // Error policy, so a `hook`-disposed action is not silently dropped.
17603        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17604        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17605        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
17606
17607        // DecliningHooks::action returns false (unhandled).
17608        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17609
17610        let error = parser
17611            .take_unknown_semantic_error()
17612            .expect("an unhandled committed action under Error policy must fail loud");
17613        let AntlrError::Unsupported(message) = error else {
17614            panic!("expected AntlrError::Unsupported, got {error:?}");
17615        };
17616        assert!(
17617            message.contains("unhandled semantic action") && message.contains("state=42"),
17618            "message should name the dropped action coordinate: {message}"
17619        );
17620
17621        // Under the default (assume-true) policy the same miss is not recorded.
17622        let mut lenient =
17623            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17624        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
17625        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17626        assert!(lenient.take_unknown_semantic_error().is_none());
17627    }
17628
17629    #[test]
17630    fn translated_predicate_is_unaffected_by_error_policy() {
17631        let atn = predicate_after_token_atn();
17632        let mut parser = mini_parser(vec![
17633            TestToken::new(1).with_text("x"),
17634            TestToken::new(2).with_text("y"),
17635            TestToken::eof("parser-test", 2, 1, 2),
17636        ]);
17637
17638        let (tree, _) = parser
17639            .parse_atn_rule_with_runtime_options(
17640                &atn,
17641                0,
17642                ParserRuntimeOptions {
17643                    predicates: &[(0, 0, ParserPredicate::True)],
17644                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17645                    ..ParserRuntimeOptions::default()
17646                },
17647            )
17648            .expect("a predicate covered by the table is not an unknown coordinate");
17649
17650        assert_eq!(parser.node(tree).text(), "xy");
17651    }
17652
17653    /// Hooks that decline (`None`) must fall through to the configured policy
17654    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
17655    /// legacy table path. Regression for the `unwrap_or(false)` that silently
17656    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
17657    fn hook_predicate_semantics() -> ParserSemantics {
17658        let mut ir = SemIr::new();
17659        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
17660        ParserSemantics {
17661            ir,
17662            predicates: vec![ParserSemanticPredicate {
17663                rule_index: 0,
17664                pred_index: 0,
17665                expr,
17666                failure_message: None,
17667            }],
17668            actions: Vec::new(),
17669        }
17670    }
17671
17672    #[derive(Debug, Default)]
17673    struct DecliningHooks;
17674
17675    impl SemanticHooks for DecliningHooks {}
17676
17677    #[test]
17678    fn semir_hook_none_falls_through_to_assume_true() {
17679        let atn = predicate_after_token_atn();
17680        let semantics = hook_predicate_semantics();
17681        let mut parser = mini_parser_with_hooks(
17682            vec![
17683                TestToken::new(1).with_text("x"),
17684                TestToken::new(2).with_text("y"),
17685                TestToken::eof("parser-test", 2, 1, 2),
17686            ],
17687            DecliningHooks,
17688        );
17689
17690        let (tree, _) = parser
17691            .parse_atn_rule_with_runtime_options(
17692                &atn,
17693                0,
17694                ParserRuntimeOptions {
17695                    semantics: Some(&semantics),
17696                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
17697                    ..ParserRuntimeOptions::default()
17698                },
17699            )
17700            .expect("a declined SemIR hook must pass under assume-true");
17701
17702        assert_eq!(parser.node(tree).text(), "xy");
17703    }
17704
17705    #[test]
17706    fn semir_hook_none_falls_through_to_assume_false() {
17707        let atn = predicate_after_token_atn();
17708        let semantics = hook_predicate_semantics();
17709        let mut parser = mini_parser_with_hooks(
17710            vec![
17711                TestToken::new(1).with_text("x"),
17712                TestToken::new(2).with_text("y"),
17713                TestToken::eof("parser-test", 2, 1, 2),
17714            ],
17715            DecliningHooks,
17716        );
17717
17718        let result = parser.parse_atn_rule_with_runtime_options(
17719            &atn,
17720            0,
17721            ParserRuntimeOptions {
17722                semantics: Some(&semantics),
17723                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17724                ..ParserRuntimeOptions::default()
17725            },
17726        );
17727
17728        assert!(
17729            result.is_err(),
17730            "a declined SemIR hook must fail the only guarded path under assume-false"
17731        );
17732    }
17733
17734    #[test]
17735    fn semir_hook_none_records_coordinate_under_error_policy() {
17736        let atn = predicate_after_token_atn();
17737        let semantics = hook_predicate_semantics();
17738        let mut parser = mini_parser_with_hooks(
17739            vec![
17740                TestToken::new(1).with_text("x"),
17741                TestToken::new(2).with_text("y"),
17742                TestToken::eof("parser-test", 2, 1, 2),
17743            ],
17744            DecliningHooks,
17745        );
17746
17747        let error = parser
17748            .parse_atn_rule_with_runtime_options(
17749                &atn,
17750                0,
17751                ParserRuntimeOptions {
17752                    semantics: Some(&semantics),
17753                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17754                    ..ParserRuntimeOptions::default()
17755                },
17756            )
17757            .expect_err("a declined SemIR hook under Error policy must fail the parse");
17758
17759        let AntlrError::Unsupported(message) = error else {
17760            panic!("expected AntlrError::Unsupported, got {error:?}");
17761        };
17762        assert!(
17763            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
17764            "message should name the unresolved coordinate: {message}"
17765        );
17766    }
17767
17768    #[test]
17769    fn generated_direct_predicate_honors_installed_policy() {
17770        // The generated recursive-descent path calls
17771        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
17772        // going through `ParserRuntimeOptions`, so the policy must be installed
17773        // via `set_unknown_predicate_policy` (as the generated constructor now
17774        // does). A declining hook must then honor it rather than the default.
17775        let semantics = hook_predicate_semantics();
17776        let context = ParserRuleContext::new(0, -1);
17777
17778        let mut assume_true =
17779            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17780        assert!(
17781            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
17782                &semantics, 0, 0, &context, 0
17783            ),
17784            "default AssumeTrue accepts a declined hook"
17785        );
17786        assert!(assume_true.take_unknown_semantic_error().is_none());
17787
17788        let mut error_policy =
17789            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17790        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17791        assert!(
17792            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
17793                &semantics, 0, 0, &context, 0
17794            ),
17795            "Error policy rejects a declined hook on the generated-direct path"
17796        );
17797        let error = error_policy
17798            .take_unknown_semantic_error()
17799            .expect("Error policy records the unresolved coordinate for the generated path");
17800        let AntlrError::Unsupported(message) = error else {
17801            panic!("expected AntlrError::Unsupported, got {error:?}");
17802        };
17803        assert!(message.contains("pred_index=0"), "message: {message}");
17804    }
17805
17806    #[test]
17807    fn parser_rule_start_skips_leading_hidden_tokens() {
17808        let atn = token_then_eof_atn();
17809        let mut parser = mini_parser(vec![
17810            TestToken::new(99)
17811                .with_text(" ")
17812                .with_channel(HIDDEN_CHANNEL),
17813            TestToken::new(1).with_text("x"),
17814            TestToken::eof("parser-test", 2, 1, 2),
17815        ]);
17816
17817        let tree = parser
17818            .parse_atn_rule(&atn, 0)
17819            .expect("artificial parser rule should parse");
17820        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
17821            panic!("rule node should be present");
17822        };
17823        assert_eq!(
17824            rule.start()
17825                .expect("rule should have a start token")
17826                .token_type(),
17827            1
17828        );
17829    }
17830
17831    #[test]
17832    fn parser_action_after_eof_stops_at_eof_token() {
17833        let atn = eof_then_action_atn();
17834        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
17835
17836        let (_, actions) = parser
17837            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17838            .expect("EOF action rule should parse");
17839
17840        assert_eq!(actions.len(), 1);
17841        assert_eq!(actions[0].stop_index(), Some(0));
17842        assert_eq!(
17843            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
17844            ""
17845        );
17846    }
17847
17848    #[test]
17849    fn after_action_stop_uses_rule_context_stop_not_cursor() {
17850        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
17851        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
17852        // but the rule did not consume it. The @after stop must follow the rule
17853        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
17854        let mut id = TestToken::new(1).with_text("x");
17855        id.set_token_index(0);
17856        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
17857        eof.set_token_index(1);
17858        let mut parser = mini_parser(vec![id.clone(), eof]);
17859        // Advance the cursor onto EOF, as it would be after `a` matched ID.
17860        parser.consume();
17861        assert_eq!(parser.la(1), TOKEN_EOF);
17862
17863        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
17864        // exactly what finish_rule(consumed_eof = false) records.
17865        let mut ctx = ParserRuleContext::new(0, 0);
17866        parser.set_context_stop(
17867            &mut ctx,
17868            parser.token_id_at(0).expect("ID token should be buffered"),
17869        );
17870        let tree = parser.rule_node(ctx);
17871
17872        let current_index = parser.input.index();
17873        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
17874        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
17875        // ...but the tree-aware helper follows the rule context stop (ID).
17876        assert_eq!(
17877            parser.after_action_stop_index_for_tree(tree, current_index),
17878            Some(0)
17879        );
17880    }
17881
17882    #[test]
17883    fn after_action_start_uses_rule_context_start_not_cursor() {
17884        // A rule that begins after leading hidden-channel tokens: the rule context
17885        // start (set by `enter_rule`) is the first visible token, not the raw cursor
17886        // that may still point at the hidden prefix. The @after start must follow
17887        // the context start so `$start`/`$text` excludes the hidden prefix.
17888        let mut parser = mini_parser(vec![
17889            TestToken::new(9)
17890                .with_text(" ")
17891                .with_channel(HIDDEN_CHANNEL),
17892            TestToken::new(9)
17893                .with_text(" ")
17894                .with_channel(HIDDEN_CHANNEL),
17895            TestToken::new(1).with_text("x"),
17896            TestToken::eof("parser-test", 3, 1, 3),
17897        ]);
17898
17899        let mut ctx = ParserRuleContext::new(0, 0);
17900        parser.set_context_start(
17901            &mut ctx,
17902            parser.token_id_at(2).expect("ID token should be buffered"),
17903        );
17904        let tree = parser.rule_node(ctx);
17905
17906        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
17907        // ...but the tree-aware helper follows the rule context start (index 2).
17908        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
17909
17910        // With no rule start recorded, it falls back to the provided index.
17911        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
17912        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
17913    }
17914
17915    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
17916        FastRecognizeOutcome {
17917            index,
17918            consumed_eof,
17919            diagnostics: DiagnosticSeqId::EMPTY,
17920            deferred_nodes: FastDeferredNodeId::EMPTY,
17921            nodes: NodeSeqId(marker),
17922        }
17923    }
17924
17925    #[test]
17926    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
17927        let mut outcomes = vec![
17928            clean_fast_outcome(4, false, 0),
17929            clean_fast_outcome(2, false, 1),
17930            clean_fast_outcome(4, false, 2),
17931            clean_fast_outcome(4, true, 3),
17932            clean_fast_outcome(2, false, 4),
17933        ];
17934        let mut scratch = FastOutcomeDedupScratch::default();
17935
17936        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17937
17938        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
17939        assert_eq!(
17940            outcomes
17941                .iter()
17942                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
17943                .collect::<Vec<_>>(),
17944            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
17945        );
17946        assert!(scratch.dense_words.is_empty());
17947        assert!(scratch.sparse_keys.is_empty());
17948    }
17949
17950    #[test]
17951    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
17952        let mut scratch = FastOutcomeDedupScratch::default();
17953        let mut outcomes = (100..109)
17954            .flat_map(|index| {
17955                [
17956                    clean_fast_outcome(
17957                        index,
17958                        false,
17959                        u32::try_from(index).expect("test index fits in u32"),
17960                    ),
17961                    clean_fast_outcome(index, false, u32::MAX),
17962                ]
17963            })
17964            .collect();
17965
17966        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
17967
17968        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
17969        assert_eq!(outcomes.len(), 9);
17970        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
17971        let dense_capacity = scratch.dense_words.capacity();
17972
17973        let mut reused = (1_000..1_009)
17974            .map(|index| {
17975                clean_fast_outcome(
17976                    index,
17977                    false,
17978                    u32::try_from(index).expect("test index fits in u32"),
17979                )
17980            })
17981            .collect();
17982        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
17983
17984        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
17985        assert_eq!(reused.len(), 9);
17986        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
17987    }
17988
17989    #[test]
17990    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
17991        let mut scratch = FastOutcomeDedupScratch::default();
17992        let sparse_indexes = [
17993            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
17994        ];
17995        let mut outcomes = sparse_indexes
17996            .into_iter()
17997            .chain([400_000])
17998            .enumerate()
17999            .map(|(marker, index)| {
18000                clean_fast_outcome(
18001                    index,
18002                    false,
18003                    u32::try_from(marker).expect("test marker fits in u32"),
18004                )
18005            })
18006            .collect();
18007
18008        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18009
18010        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18011        assert_eq!(outcomes.len(), sparse_indexes.len());
18012        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
18013        let sparse_capacity = scratch.sparse_keys.capacity();
18014
18015        let mut reused = sparse_indexes
18016            .into_iter()
18017            .map(|index| {
18018                clean_fast_outcome(
18019                    index,
18020                    false,
18021                    u32::try_from(index).expect("test index fits in u32"),
18022                )
18023            })
18024            .collect();
18025        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
18026
18027        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18028        assert_eq!(reused.len(), sparse_indexes.len());
18029        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
18030    }
18031
18032    #[test]
18033    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
18034        let mut scratch = FastOutcomeDedupScratch::default();
18035        scratch
18036            .sparse_keys
18037            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
18038        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
18039        let mut outcomes = (0..9)
18040            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
18041            .collect();
18042
18043        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18044
18045        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18046        assert!(scratch.sparse_keys.is_empty());
18047        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
18048    }
18049
18050    #[test]
18051    fn fast_outcome_selection_respects_sll_tie_order() {
18052        let mut arena = RecognitionArena::default();
18053        let first = FastRecognizeOutcome {
18054            index: 1,
18055            consumed_eof: false,
18056            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18057                line: 1,
18058                column: 0,
18059                message: "mismatched input 'x'".to_owned(),
18060                offending: None,
18061            }]),
18062            deferred_nodes: FastDeferredNodeId::EMPTY,
18063            nodes: NodeSeqId::EMPTY,
18064        };
18065        let second = FastRecognizeOutcome {
18066            index: first.index,
18067            consumed_eof: first.consumed_eof,
18068            diagnostics: DiagnosticSeqId::EMPTY,
18069            deferred_nodes: FastDeferredNodeId::EMPTY,
18070            nodes: NodeSeqId::EMPTY,
18071        };
18072
18073        let selected = select_best_fast_outcome(
18074            [first, second].into_iter(),
18075            PredictionMode::Sll,
18076            None,
18077            |_| panic!("caller-follow token probe should not run"),
18078            &arena,
18079        )
18080        .expect("one outcome should be selected");
18081        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
18082        let eof_second = FastRecognizeOutcome {
18083            index: second.index,
18084            consumed_eof: true,
18085            diagnostics: DiagnosticSeqId::EMPTY,
18086            deferred_nodes: FastDeferredNodeId::EMPTY,
18087            nodes: NodeSeqId::EMPTY,
18088        };
18089        let selected = select_best_fast_outcome(
18090            [first, eof_second].into_iter(),
18091            PredictionMode::Sll,
18092            None,
18093            |_| panic!("caller-follow token probe should not run"),
18094            &arena,
18095        )
18096        .expect("one outcome should be selected");
18097        assert!(!selected.consumed_eof);
18098        let selected = select_best_fast_outcome(
18099            [first, second].into_iter(),
18100            PredictionMode::Ll,
18101            None,
18102            |_| panic!("caller-follow token probe should not run"),
18103            &arena,
18104        )
18105        .expect("one outcome should be selected");
18106        assert!(selected.diagnostics.is_empty());
18107    }
18108
18109    #[test]
18110    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
18111        let mut arena = RecognitionArena::default();
18112        let first = FastRecognizeOutcome {
18113            index: 3,
18114            consumed_eof: false,
18115            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18116                line: 1,
18117                column: 0,
18118                message: "mismatched input 'x' expecting 'a'".to_owned(),
18119                offending: None,
18120            }]),
18121            deferred_nodes: FastDeferredNodeId::EMPTY,
18122            nodes: NodeSeqId::EMPTY,
18123        };
18124        let same_rank = FastRecognizeOutcome {
18125            index: first.index,
18126            consumed_eof: first.consumed_eof,
18127            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18128                line: 1,
18129                column: 0,
18130                message: "mismatched input 'x' expecting 'b'".to_owned(),
18131                offending: None,
18132            }]),
18133            deferred_nodes: FastDeferredNodeId::EMPTY,
18134            nodes: NodeSeqId::EMPTY,
18135        };
18136        let better_rank = FastRecognizeOutcome {
18137            index: first.index,
18138            consumed_eof: first.consumed_eof,
18139            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18140                line: 1,
18141                column: 0,
18142                message: "missing 'a' at 'x'".to_owned(),
18143                offending: None,
18144            }]),
18145            deferred_nodes: FastDeferredNodeId::EMPTY,
18146            nodes: NodeSeqId::EMPTY,
18147        };
18148        let mut outcomes = vec![first, same_rank, better_rank];
18149
18150        dedupe_fast_outcomes(&mut outcomes, &arena);
18151
18152        assert_eq!(outcomes.len(), 2);
18153        assert_eq!(
18154            arena
18155                .diagnostics(outcomes[0].diagnostics)
18156                .next()
18157                .expect("first diagnostic")
18158                .message,
18159            "mismatched input 'x' expecting 'a'"
18160        );
18161        assert_eq!(
18162            arena
18163                .diagnostics(outcomes[1].diagnostics)
18164                .next()
18165                .expect("second diagnostic")
18166                .message,
18167            "missing 'a' at 'x'"
18168        );
18169    }
18170
18171    #[test]
18172    fn fast_outcome_selection_prefers_generated_caller_follow() {
18173        let arena = RecognitionArena::default();
18174        let earlier = FastRecognizeOutcome {
18175            index: 7,
18176            consumed_eof: false,
18177            diagnostics: DiagnosticSeqId::EMPTY,
18178            deferred_nodes: FastDeferredNodeId::EMPTY,
18179            nodes: NodeSeqId::EMPTY,
18180        };
18181        let later = FastRecognizeOutcome {
18182            index: 8,
18183            consumed_eof: false,
18184            diagnostics: DiagnosticSeqId::EMPTY,
18185            deferred_nodes: FastDeferredNodeId::EMPTY,
18186            nodes: NodeSeqId::EMPTY,
18187        };
18188        let mut follow = TokenBitSet::default();
18189        follow.insert(5);
18190
18191        let selected = select_best_fast_outcome(
18192            [later, earlier].into_iter(),
18193            PredictionMode::Ll,
18194            Some(&follow),
18195            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
18196            &arena,
18197        )
18198        .expect("one outcome should be selected");
18199        assert_eq!(selected.index, 7);
18200
18201        let selected = select_best_fast_outcome(
18202            [later, earlier].into_iter(),
18203            PredictionMode::Ll,
18204            Some(&follow),
18205            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
18206            &arena,
18207        )
18208        .expect("one outcome should be selected");
18209        assert_eq!(selected.index, 8);
18210
18211        let indented_next_statement = FastRecognizeOutcome {
18212            index: 9,
18213            consumed_eof: false,
18214            diagnostics: DiagnosticSeqId::EMPTY,
18215            deferred_nodes: FastDeferredNodeId::EMPTY,
18216            nodes: NodeSeqId::EMPTY,
18217        };
18218        let selected = select_best_fast_outcome(
18219            [indented_next_statement, earlier].into_iter(),
18220            PredictionMode::Ll,
18221            Some(&follow),
18222            |index| {
18223                let is_boundary = index == 7;
18224                let is_boundary_gap = matches!(index, 7 | 8);
18225                (
18226                    if index == 7 { 5 } else { TOKEN_EOF },
18227                    is_boundary,
18228                    is_boundary_gap,
18229                )
18230            },
18231            &arena,
18232        )
18233        .expect("one outcome should be selected");
18234        assert_eq!(selected.index, 7);
18235
18236        let continuation = FastRecognizeOutcome {
18237            index: 10,
18238            consumed_eof: false,
18239            diagnostics: DiagnosticSeqId::EMPTY,
18240            deferred_nodes: FastDeferredNodeId::EMPTY,
18241            nodes: NodeSeqId::EMPTY,
18242        };
18243        let selected = select_best_fast_outcome(
18244            [continuation, earlier].into_iter(),
18245            PredictionMode::Ll,
18246            Some(&follow),
18247            |index| {
18248                let is_boundary = matches!(index, 7 | 9);
18249                (
18250                    if index == 7 { 5 } else { TOKEN_EOF },
18251                    is_boundary,
18252                    is_boundary,
18253                )
18254            },
18255            &arena,
18256        )
18257        .expect("one outcome should be selected");
18258        assert_eq!(selected.index, 10);
18259
18260        let selected = select_best_fast_outcome(
18261            [earlier, later].into_iter(),
18262            PredictionMode::Sll,
18263            Some(&follow),
18264            |_| panic!("caller-follow token probe should not run in SLL mode"),
18265            &arena,
18266        )
18267        .expect("one outcome should be selected");
18268        assert_eq!(selected.index, 8);
18269    }
18270
18271    #[test]
18272    fn caller_follow_boundary_text_requires_separator_shape() {
18273        assert!(is_caller_follow_boundary_text(";"));
18274        assert!(is_caller_follow_boundary_text("\n"));
18275        assert!(is_caller_follow_boundary_text("\r\n  "));
18276        assert!(is_caller_follow_boundary_text(";\n"));
18277        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
18278        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
18279        assert!(!is_caller_follow_boundary_text("identifier"));
18280        assert!(is_caller_follow_boundary_gap_text(" \t "));
18281        assert!(is_caller_follow_boundary_gap_text("\n  "));
18282        assert!(is_caller_follow_boundary_gap_text(";\t"));
18283        assert!(!is_caller_follow_boundary_gap_text(
18284            "\"\"\"line1\nline2\"\"\""
18285        ));
18286        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
18287    }
18288
18289    #[test]
18290    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
18291        let mut parser = mini_parser(vec![
18292            TestToken::new(5).with_text("\n"),
18293            TestToken::new(6)
18294                .with_text("// comment\n")
18295                .with_channel(HIDDEN_CHANNEL),
18296            TestToken::new(1).with_text("x"),
18297            TestToken::eof("parser-test", 1, 2, 0),
18298        ]);
18299
18300        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
18301        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
18302        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
18303    }
18304
18305    #[test]
18306    fn caller_follow_token_info_uses_stream_visible_channel() {
18307        let source = Source {
18308            tokens: vec![
18309                TestToken::new(5).with_text("\n").with_channel(2),
18310                TestToken::new(1).with_text("x").with_channel(2),
18311                TestToken::new(6)
18312                    .with_text("// comment\n")
18313                    .with_channel(HIDDEN_CHANNEL),
18314                TestToken::eof("parser-test", 1, 2, 0),
18315            ],
18316            index: 0,
18317        };
18318        let data = RecognizerData::new(
18319            "Mini.g4",
18320            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18321        );
18322        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
18323
18324        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
18325        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
18326        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
18327    }
18328
18329    #[test]
18330    fn reset_per_parse_caches_clears_state_expected_token_cache() {
18331        let atn = token_then_eof_atn();
18332        let mut parser = mini_parser(Vec::new());
18333
18334        let _ = parser.cached_state_expected_token_set(&atn, 0);
18335        assert!(!parser.state_expected_token_cache.is_empty());
18336
18337        parser.reset_per_parse_caches();
18338        assert!(parser.state_expected_token_cache.is_empty());
18339    }
18340
18341    #[test]
18342    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
18343        let cyclic = epsilon_cycle_atn();
18344        let acyclic = token_then_eof_atn();
18345        let mut parser = mini_parser(Vec::new());
18346
18347        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
18348        assert_eq!(
18349            parser.empty_cycle_cache_atn,
18350            Some(SharedAtnCacheKey::for_atn(&cyclic))
18351        );
18352        assert_eq!(parser.empty_cycle_cache[1], Some(true));
18353
18354        parser.reset_per_parse_caches();
18355        assert_eq!(parser.empty_cycle_cache[1], Some(true));
18356        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
18357
18358        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
18359        assert_eq!(
18360            parser.empty_cycle_cache_atn,
18361            Some(SharedAtnCacheKey::for_atn(&acyclic))
18362        );
18363        assert_eq!(parser.empty_cycle_cache[1], Some(false));
18364    }
18365
18366    #[test]
18367    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
18368        let source = Source {
18369            tokens: vec![
18370                TestToken::new(1).with_text("x"),
18371                TestToken::eof("parser-test", 1, 1, 1),
18372            ],
18373            index: 0,
18374        };
18375        let data = RecognizerData::new(
18376            "Mini.g4",
18377            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18378        );
18379        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
18380        let expected = ExpectedTokens {
18381            index: Some(0),
18382            symbols: BTreeSet::new(),
18383            no_viable: None,
18384        };
18385
18386        let (_, message) = parser.expected_error_message(0, 0, &expected);
18387
18388        assert_eq!(message, "mismatched input 'x'");
18389    }
18390
18391    #[test]
18392    fn eof_rule_stop_index_points_at_eof_token() {
18393        let source = Source {
18394            tokens: vec![
18395                TestToken::new(1).with_text("x"),
18396                TestToken::eof("parser-test", 1, 1, 1),
18397            ],
18398            index: 0,
18399        };
18400        let data = RecognizerData::new(
18401            "Mini.g4",
18402            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18403        );
18404        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
18405
18406        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
18407        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
18408    }
18409
18410    #[test]
18411    fn generated_parser_action_uses_current_rule_stop_boundary() {
18412        let mut parser = mini_parser(vec![
18413            TestToken::new(1).with_text("x"),
18414            TestToken::eof("parser-test", 1, 1, 1),
18415        ]);
18416
18417        parser.match_token(1).expect("token should match");
18418        let action = parser.parser_action_at_current(7, 0, 0, false);
18419        assert_eq!(action.source_state(), 7);
18420        assert_eq!(action.rule_index(), 0);
18421        assert_eq!(action.start_index(), 0);
18422        assert_eq!(action.stop_index(), Some(0));
18423
18424        parser.match_eof().expect("EOF should match");
18425        let action = parser.parser_action_at_current(8, 0, 0, true);
18426        assert_eq!(action.stop_index(), Some(1));
18427    }
18428
18429    #[test]
18430    fn folds_left_recursive_boundary_into_rule_node() {
18431        let mut arena = RecognitionArena::default();
18432        let first = arena.push_node(ArenaRecognizedNode::Token {
18433            token: TokenId::try_from(0).expect("test token ID"),
18434        });
18435        let boundary = arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
18436            rule_index: 1,
18437            alt_number: 3,
18438        });
18439        let second = arena.push_node(ArenaRecognizedNode::Token {
18440            token: TokenId::try_from(1).expect("test token ID"),
18441        });
18442        let mut nodes = NodeSeqId::EMPTY;
18443        for node in [first, boundary, second].into_iter().rev() {
18444            nodes = arena.prepend(nodes, node);
18445        }
18446
18447        let folded = arena.fold_left_recursive_boundaries(nodes);
18448        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
18449
18450        assert_eq!(folded_nodes.len(), 2);
18451        let ArenaRecognizedNode::Rule {
18452            rule_index,
18453            invoking_state,
18454            alt_number,
18455            start_index,
18456            stop_index,
18457            children,
18458            ..
18459        } = arena.node(folded_nodes[0])
18460        else {
18461            panic!("first folded node should be a rule");
18462        };
18463        // The folded rule node's scalar shape (rule/invoking-state/alt/start/stop) is one snapshot;
18464        // child resolution and the sibling identity below stay explicit — a node Debug prints the
18465        // children handle, not the resolved sequence they assert on.
18466        insta::assert_debug_snapshot!(
18467            "folds_left_recursive_boundary_into_rule_node",
18468            (
18469                rule_index,
18470                invoking_state,
18471                alt_number,
18472                start_index,
18473                stop_index
18474            )
18475        );
18476        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
18477        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
18478
18479        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
18480        assert_eq!(
18481            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18482            (4, 3, 1)
18483        );
18484        assert_eq!(
18485            (stats.total_links, stats.live_links, stats.dead_links),
18486            (9, 3, 6)
18487        );
18488    }
18489
18490    #[test]
18491    fn recognition_arena_reports_live_dead_and_retained_capacity() {
18492        let mut arena = RecognitionArena::default();
18493        let token = arena.push_node(ArenaRecognizedNode::Token {
18494            token: TokenId::try_from(0).expect("test token ID"),
18495        });
18496        let extra = arena.push_extra(RecognitionExtra::MissingToken {
18497            token_type: 2,
18498            at_index: 1,
18499            text: "<missing X>".to_owned(),
18500        });
18501        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
18502        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
18503            token: TokenId::try_from(1).expect("test token ID"),
18504        });
18505        let mut live = NodeSeqId::EMPTY;
18506        live = arena.prepend(live, missing);
18507        live = arena.prepend(live, token);
18508        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
18509        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18510            line: 1,
18511            column: 0,
18512            message: "missing X".to_owned(),
18513            offending: None,
18514        }]);
18515        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18516            line: 1,
18517            column: 1,
18518            message: "discarded".to_owned(),
18519            offending: None,
18520        }]);
18521        let deferred_children = arena.deferred_fragment(live);
18522        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
18523            rule_index: 0,
18524            invoking_state: -1,
18525            start_index: 0,
18526            stop_index: Some(1),
18527            deferred_children,
18528            children: NodeSeqId::EMPTY,
18529        });
18530
18531        let stats = arena.stats(live, live_diagnostics);
18532
18533        assert_eq!(
18534            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18535            (3, 2, 1)
18536        );
18537        assert_eq!(
18538            (stats.total_links, stats.live_links, stats.dead_links),
18539            (5, 3, 2)
18540        );
18541        assert_eq!(
18542            (stats.total_extras, stats.live_extras, stats.dead_extras),
18543            (3, 2, 1)
18544        );
18545        assert!(size_of::<SeqLink>() <= 8);
18546        assert!(size_of::<DiagnosticLink>() <= 8);
18547        assert!(size_of::<FastDeferredNode>() <= 12);
18548        assert!(size_of::<FastDeferredRule>() <= 28);
18549        assert!(size_of::<FastRecognizeOutcome>() <= 24);
18550        let capacities = (
18551            stats.node_capacity,
18552            stats.link_capacity,
18553            stats.extra_capacity,
18554        );
18555        let deferred_capacities = (
18556            arena.deferred_nodes.capacity(),
18557            arena.deferred_rules.capacity(),
18558        );
18559
18560        arena.reset();
18561        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
18562        assert_eq!(
18563            (reset.total_nodes, reset.total_links, reset.total_extras),
18564            (0, 0, 0)
18565        );
18566        assert_eq!(
18567            (
18568                reset.node_capacity,
18569                reset.link_capacity,
18570                reset.extra_capacity,
18571            ),
18572            capacities
18573        );
18574        assert!(arena.deferred_nodes.is_empty());
18575        assert!(arena.deferred_rules.is_empty());
18576        assert_eq!(
18577            (
18578                arena.deferred_nodes.capacity(),
18579                arena.deferred_rules.capacity(),
18580            ),
18581            deferred_capacities
18582        );
18583    }
18584
18585    #[test]
18586    fn parser_computes_recognition_arena_stats_on_demand() {
18587        let mut parser = mini_parser(Vec::new());
18588        let live = parser
18589            .recognition_arena
18590            .push_node(ArenaRecognizedNode::Token {
18591                token: TokenId::try_from(0).expect("test token ID"),
18592            });
18593        let discarded = parser
18594            .recognition_arena
18595            .push_node(ArenaRecognizedNode::ErrorToken {
18596                token: TokenId::try_from(1).expect("test token ID"),
18597            });
18598        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
18599        let _discarded_root = parser
18600            .recognition_arena
18601            .prepend(NodeSeqId::EMPTY, discarded);
18602        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
18603
18604        let stats = parser.recognition_arena_stats();
18605
18606        assert_eq!(
18607            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18608            (2, 1, 1)
18609        );
18610        assert_eq!(
18611            (stats.total_links, stats.live_links, stats.dead_links),
18612            (2, 1, 1)
18613        );
18614    }
18615
18616    #[test]
18617    fn recognition_arena_drops_capacity_above_retention_limit() {
18618        let mut storage = Vec::<u8>::with_capacity(4);
18619        storage.extend([1, 2, 3]);
18620
18621        reset_arena_vec(&mut storage, 3);
18622
18623        assert!(storage.is_empty());
18624        assert_eq!(storage.capacity(), 0);
18625    }
18626
18627    #[test]
18628    fn recognition_arena_concatenates_diagnostics_in_source_order() {
18629        let mut arena = RecognitionArena::default();
18630        let prefix = arena.diagnostic_sequence([
18631            ParserDiagnostic {
18632                line: 1,
18633                column: 0,
18634                message: "first".to_owned(),
18635                offending: None,
18636            },
18637            ParserDiagnostic {
18638                line: 1,
18639                column: 1,
18640                message: "second".to_owned(),
18641                offending: None,
18642            },
18643        ]);
18644        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
18645            line: 1,
18646            column: 2,
18647            message: "third".to_owned(),
18648            offending: None,
18649        }]);
18650        let extras_before = arena.extras.len();
18651
18652        let combined = arena.concat_diagnostics(prefix, suffix);
18653        let messages = arena
18654            .diagnostics(combined)
18655            .map(|diagnostic| diagnostic.message.as_str())
18656            .collect::<Vec<_>>();
18657
18658        assert_eq!(messages, ["first", "second", "third"]);
18659        assert_eq!(arena.extras.len(), extras_before);
18660    }
18661
18662    #[test]
18663    fn outcome_ties_keep_later_non_recursive_alternative() {
18664        let arena = RecognitionArena::default();
18665        let first = RecognizeOutcome {
18666            index: 1,
18667            consumed_eof: false,
18668            alt_number: 0,
18669            member_values: BTreeMap::new(),
18670            return_values: BTreeMap::new(),
18671            diagnostics: DiagnosticSeqId::EMPTY,
18672            decisions: Vec::new(),
18673            actions: vec![ParserAction::new(1, 0, 0, None)],
18674            nodes: NodeSeqId::EMPTY,
18675        };
18676        let second = RecognizeOutcome {
18677            actions: vec![ParserAction::new(2, 0, 0, None)],
18678            ..first.clone()
18679        };
18680
18681        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18682            .expect("one outcome should be selected");
18683        assert_eq!(selected.actions[0].source_state(), 2);
18684    }
18685
18686    #[test]
18687    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
18688        let arena = RecognitionArena::default();
18689        let first = RecognizeOutcome {
18690            index: 1,
18691            consumed_eof: false,
18692            alt_number: 0,
18693            member_values: BTreeMap::new(),
18694            return_values: BTreeMap::new(),
18695            diagnostics: DiagnosticSeqId::EMPTY,
18696            decisions: Vec::new(),
18697            actions: vec![ParserAction::new(1, 0, 0, None)],
18698            nodes: NodeSeqId::EMPTY,
18699        };
18700        let second = RecognizeOutcome {
18701            actions: vec![
18702                ParserAction::new(2, 0, 0, None),
18703                ParserAction::new(3, 0, 0, None),
18704            ],
18705            ..first.clone()
18706        };
18707
18708        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
18709            .expect("one outcome should be selected");
18710        assert_eq!(selected.actions.len(), 2);
18711    }
18712
18713    #[test]
18714    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
18715        let arena = RecognitionArena::default();
18716        let first = RecognizeOutcome {
18717            index: 7,
18718            consumed_eof: false,
18719            alt_number: 0,
18720            member_values: BTreeMap::new(),
18721            return_values: BTreeMap::new(),
18722            diagnostics: DiagnosticSeqId::EMPTY,
18723            decisions: vec![1, 0],
18724            actions: vec![
18725                ParserAction::new(23, 2, 2, Some(4)),
18726                ParserAction::new(23, 2, 0, Some(6)),
18727            ],
18728            nodes: NodeSeqId::EMPTY,
18729        };
18730        let second = RecognizeOutcome {
18731            decisions: vec![0, 1],
18732            actions: vec![
18733                ParserAction::new(23, 2, 2, Some(6)),
18734                ParserAction::new(23, 2, 0, Some(6)),
18735            ],
18736            ..first.clone()
18737        };
18738
18739        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18740            .expect("one outcome should be selected");
18741        assert_eq!(selected.actions[0].stop_index(), Some(6));
18742    }
18743
18744    #[test]
18745    fn outcome_ties_keep_first_recursive_tree_shape() {
18746        let mut arena = RecognitionArena::default();
18747        let token = arena.push_node(ArenaRecognizedNode::Token {
18748            token: TokenId::try_from(0).expect("test token ID"),
18749        });
18750        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
18751        let inner = arena.push_node(ArenaRecognizedNode::Rule {
18752            rule_index: 1,
18753            invoking_state: -1,
18754            alt_number: 0,
18755            start_index: 0,
18756            stop_index: Some(0),
18757            return_values: None,
18758            children: token_children,
18759        });
18760        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
18761        let outer = arena.push_node(ArenaRecognizedNode::Rule {
18762            rule_index: 1,
18763            invoking_state: -1,
18764            alt_number: 0,
18765            start_index: 0,
18766            stop_index: Some(0),
18767            return_values: None,
18768            children: inner_children,
18769        });
18770        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
18771        let first = RecognizeOutcome {
18772            index: 1,
18773            consumed_eof: false,
18774            alt_number: 0,
18775            member_values: BTreeMap::new(),
18776            return_values: BTreeMap::new(),
18777            diagnostics: DiagnosticSeqId::EMPTY,
18778            decisions: Vec::new(),
18779            actions: vec![ParserAction::new(1, 0, 0, None)],
18780            nodes: recursive_nodes,
18781        };
18782        let second = RecognizeOutcome {
18783            index: 1,
18784            consumed_eof: false,
18785            alt_number: 0,
18786            member_values: BTreeMap::new(),
18787            return_values: BTreeMap::new(),
18788            diagnostics: DiagnosticSeqId::EMPTY,
18789            decisions: Vec::new(),
18790            actions: vec![ParserAction::new(2, 0, 0, None)],
18791            nodes: recursive_nodes,
18792        };
18793
18794        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18795            .expect("one outcome should be selected");
18796        assert_eq!(selected.actions[0].source_state(), 1);
18797    }
18798
18799    #[test]
18800    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
18801        let mut arena = RecognitionArena::default();
18802        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18803            line: 1,
18804            column: 3,
18805            message: "missing 'Y' at '<EOF>'".to_owned(),
18806            offending: None,
18807        }]);
18808        let first_alt = RecognizeOutcome {
18809            index: 2,
18810            consumed_eof: true,
18811            alt_number: 0,
18812            member_values: BTreeMap::new(),
18813            return_values: BTreeMap::new(),
18814            diagnostics: recovered_diagnostics,
18815            decisions: vec![0],
18816            actions: vec![ParserAction::new(1, 0, 0, None)],
18817            nodes: NodeSeqId::EMPTY,
18818        };
18819        let second_alt = RecognizeOutcome {
18820            diagnostics: DiagnosticSeqId::EMPTY,
18821            decisions: vec![1],
18822            actions: vec![ParserAction::new(2, 0, 0, None)],
18823            ..first_alt.clone()
18824        };
18825
18826        let selected = select_best_outcome(
18827            [second_alt, first_alt].into_iter(),
18828            PredictionMode::Sll,
18829            &arena,
18830        )
18831        .expect("one outcome should be selected");
18832        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
18833        assert_eq!(selected.decisions, [0]);
18834    }
18835}