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, SyntaxErrorEvent};
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, MemberEnv, 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 MemberEnv,
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.scalar(member)
618    }
619
620    /// Top of a stack-valued member slot on the current speculative path;
621    /// `None` when the stack is empty or was never pushed.
622    #[must_use]
623    pub fn member_stack_top(&self, member: usize) -> Option<i64> {
624        self.member_values.stack_top(member)
625    }
626
627    /// Depth of a stack-valued member slot on the current speculative path.
628    #[must_use]
629    pub fn member_stack_len(&self, member: usize) -> usize {
630        self.member_values.stack_len(member)
631    }
632
633    /// Parser action event being replayed, when this context belongs to an
634    /// action hook.
635    #[must_use]
636    pub const fn action(&self) -> Option<ParserAction> {
637        self.action
638    }
639
640    /// Text covered by a parser action event.
641    ///
642    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
643    /// EOF the interval ends at the previous *visible* token, so trailing hidden
644    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
645    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
646    /// itself guards `start > stop`, so an empty interval yields `""`.
647    pub fn action_text(&self) -> String {
648        let Some(action) = self.action else {
649            return String::new();
650        };
651        let Some(stop) = action.stop_index() else {
652            return String::new();
653        };
654        let stop = if self
655            .input
656            .get(stop)
657            .is_some_and(|token| token.token_type() == TOKEN_EOF)
658        {
659            let Some(previous) = self.input.previous_visible_token_index(stop) else {
660                return String::new();
661            };
662            previous
663        } else {
664            stop
665        };
666        self.input.text(action.start_index(), stop)
667    }
668}
669
670/// User extension point for parser semantic predicates and actions that the
671/// metadata generator did not translate into built-in runtime metadata.
672///
673/// Returning `None`/`false` says "not handled", so the runtime falls through
674/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
675/// speculative prediction and must be replay-safe.
676pub trait SemanticHooks {
677    /// Whether generated lexers should route lifecycle callbacks through this
678    /// hook object.
679    ///
680    /// User hook implementations opt in by default. [`NoSemanticHooks`]
681    /// overrides this to keep generated lexers on the direct no-extension
682    /// token path.
683    const ENABLES_LEXER_LIFECYCLE: bool = true;
684
685    /// Whether this hook object may observe parser predicate transitions.
686    ///
687    /// Custom hooks default to conservative predicate handling so the fast
688    /// recognizer does not bypass a `sempred` implementation.
689    fn observes_parser_predicates(&self) -> bool {
690        true
691    }
692
693    /// Whether this hook object may override interpreted parser decisions.
694    ///
695    /// This remains disabled by default so ordinary generated parsers retain
696    /// the fast recognizer path.
697    fn observes_parser_decisions(&self) -> bool {
698        false
699    }
700
701    /// Overrides one interpreted parser decision with a one-based alternative.
702    ///
703    /// Returning `None` leaves normal adaptive prediction in control. Hooks
704    /// that return an alternative own any one-shot or input-index filtering
705    /// they require.
706    fn parser_decision_override(
707        &mut self,
708        decision: usize,
709        input_index: usize,
710        alternative_count: usize,
711    ) -> Option<usize> {
712        let _ = (decision, input_index, alternative_count);
713        None
714    }
715
716    fn sempred<S>(
717        &mut self,
718        ctx: &mut ParserSemCtx<'_, S>,
719        rule_index: usize,
720        pred_index: usize,
721    ) -> Option<bool>
722    where
723        S: TokenSource,
724    {
725        let _ = (ctx, rule_index, pred_index);
726        None
727    }
728
729    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
730    where
731        S: TokenSource,
732    {
733        let _ = (ctx, action);
734        false
735    }
736
737    fn lexer_sempred<I>(
738        &mut self,
739        ctx: &mut LexerSemCtx<'_, I>,
740        rule_index: usize,
741        pred_index: usize,
742    ) -> Option<bool>
743    where
744        I: CharStream,
745    {
746        let _ = (ctx, rule_index, pred_index);
747        None
748    }
749
750    /// Runs a lexer custom action on the committed lexing path. Returns whether
751    /// the hook handled the action.
752    ///
753    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
754    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
755    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
756    /// queued prefix tokens, just like the closure-based `custom_action` API.
757    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
758    /// borrow, so those mutators are inert there.)
759    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
760    where
761        I: CharStream,
762    {
763        let _ = (ctx, action);
764        false
765    }
766
767    /// Runs after runtime-owned lexer state has been reset for reuse.
768    ///
769    /// Implementations should clear extension-owned transient state here.
770    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
771    where
772        I: CharStream,
773    {
774        let _ = ctx;
775    }
776
777    /// Runs before the runtime returns a queued token or starts a new ATN
778    /// token match.
779    ///
780    /// The callback also runs between internal `skip`/`more` matches, so it
781    /// observes every point where another ATN match may start.
782    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
783    where
784        I: CharStream,
785    {
786        let _ = ctx;
787    }
788
789    /// Runs after the accepted path's portable and custom actions, but before
790    /// the token span is finalized and emitted.
791    ///
792    /// Accepted paths that selected `skip` or `more` are included, and the hook
793    /// may observe or override that pending token type.
794    ///
795    /// This callback has no synthetic ATN coordinate. It therefore also runs
796    /// for accepted rules that contain no action or predicate.
797    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
798    where
799        I: CharStream,
800    {
801        let _ = ctx;
802    }
803
804    /// Observes a token after committed lexer actions and portable commands
805    /// have run and the token has been emitted, immediately before it is
806    /// returned to the token stream.
807    ///
808    /// Hidden and custom-channel tokens are included. `skip` and intermediate
809    /// `more` matches do not produce callbacks.
810    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
811        let _ = token;
812    }
813}
814
815/// Default hook object used by parsers that do not need user-supplied
816/// semantics.
817#[derive(Clone, Copy, Debug, Default)]
818pub struct NoSemanticHooks;
819
820impl SemanticHooks for NoSemanticHooks {
821    const ENABLES_LEXER_LIFECYCLE: bool = false;
822
823    fn observes_parser_predicates(&self) -> bool {
824        false
825    }
826}
827
828/// Parser semantic predicate rendered from a supported target template.
829///
830/// The metadata recognizer evaluates these at the token-stream index where the
831/// predicate transition is reached. Unsupported or absent predicate templates
832/// remain unconditional so existing generated parsers keep their previous
833/// behavior unless the generator opts into this table.
834#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
835pub enum ParserPredicate {
836    True,
837    False,
838    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
839    FalseWithMessage {
840        message: &'static str,
841    },
842    /// Target-template test helper that reports predicate evaluation before
843    /// returning the wrapped boolean value.
844    Invoke {
845        value: bool,
846    },
847    LookaheadTextEquals {
848        offset: isize,
849        text: &'static str,
850    },
851    LookaheadNotEquals {
852        offset: isize,
853        token_type: i32,
854    },
855    /// Checks that the last two consumed visible tokens were adjacent in the
856    /// token stream. Used by C# parser predicates for split operator tokens.
857    TokenPairAdjacent,
858    /// Checks a generated parser context child by rule index and text.
859    ///
860    /// If the child is absent the predicate succeeds, matching target helpers
861    /// that treat incomplete or non-matching contexts as non-restrictive.
862    ContextChildRuleTextNotEquals {
863        rule_index: usize,
864        text: &'static str,
865    },
866    /// Compares the current rule invocation's integer argument with a literal
867    /// value from a supported `ValEquals("$i", "...")` target template.
868    LocalIntEquals {
869        value: i64,
870    },
871    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
872    /// rule invocation's integer argument.
873    LocalIntLessOrEqual {
874        value: i64,
875    },
876    /// Compares a generated parser integer member modulo a literal value.
877    MemberModuloEquals {
878        member: usize,
879        modulus: i64,
880        value: i64,
881        equals: bool,
882    },
883    /// Compares a generated parser integer member with a literal value.
884    MemberEquals {
885        member: usize,
886        value: i64,
887        equals: bool,
888    },
889}
890
891impl ParserPredicate {
892    /// Lowers the legacy predicate metadata variant into `SemIR`.
893    ///
894    /// This is the compatibility adapter for generated parsers produced while
895    /// the runtime still emitted closed enum tables. Newer generated parsers
896    /// emit `SemIR` directly.
897    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
898        match self {
899            Self::True => ir.expr(PExpr::Bool(true)),
900            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
901            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
902            Self::LookaheadTextEquals { offset, text } => {
903                let token = ir.expr(PExpr::TokenText(offset));
904                let text = ir.intern(text);
905                let text = ir.expr(PExpr::Str(text));
906                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
907            }
908            Self::LookaheadNotEquals { offset, token_type } => {
909                let actual = ir.expr(PExpr::La(offset));
910                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
911                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
912            }
913            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
914            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
915                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
916                let expected = ir.intern(text);
917                let expected = ir.expr(PExpr::Str(expected));
918                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
919            }
920            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
921            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
922            Self::MemberModuloEquals {
923                member,
924                modulus,
925                value,
926                equals,
927            } => {
928                if modulus == 0 {
929                    return ir.expr(PExpr::Bool(false));
930                }
931                let member = ir.expr(PExpr::Member(member));
932                let modulus = ir.expr(PExpr::Int(modulus));
933                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
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            Self::MemberEquals {
942                member,
943                value,
944                equals,
945            } => {
946                let actual = ir.expr(PExpr::Member(member));
947                let expected = ir.expr(PExpr::Int(value));
948                ir.expr(PExpr::Cmp(
949                    if equals { CmpOp::Eq } else { CmpOp::Ne },
950                    actual,
951                    expected,
952                ))
953            }
954        }
955    }
956
957    #[must_use]
958    pub const fn failure_message(self) -> Option<&'static str> {
959        match self {
960            Self::FalseWithMessage { message } => Some(message),
961            Self::True
962            | Self::False
963            | Self::Invoke { .. }
964            | Self::LookaheadTextEquals { .. }
965            | Self::LookaheadNotEquals { .. }
966            | Self::TokenPairAdjacent
967            | Self::ContextChildRuleTextNotEquals { .. }
968            | Self::LocalIntEquals { .. }
969            | Self::LocalIntLessOrEqual { .. }
970            | Self::MemberModuloEquals { .. }
971            | Self::MemberEquals { .. } => None,
972        }
973    }
974}
975
976fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
977    let local = ir.expr(PExpr::LocalArg);
978    let absent = ir.expr(PExpr::IsNull(local));
979    let expected = ir.expr(PExpr::Int(value));
980    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
981    ir.expr(PExpr::Or([absent, comparison].into()))
982}
983
984/// Policy for semantic predicate coordinates that have no runtime
985/// implementation.
986///
987/// ANTLR grammars may embed target-language predicates that the metadata
988/// generator could not translate into a [`ParserPredicate`] table entry. When
989/// recognition reaches such a coordinate the runtime cannot know the grammar
990/// author's intent, so the caller chooses how to proceed.
991///
992/// The default is [`Self::AssumeTrue`], matching the historical behavior of
993/// this runtime. That default is deprecated and will change to [`Self::Error`]
994/// in a future minor release; grammars relying on unconditional predicates
995/// should opt in explicitly.
996#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
997pub enum UnknownSemanticPolicy {
998    /// Treat the predicate as passing, as if it were absent from the grammar.
999    #[default]
1000    AssumeTrue,
1001    /// Treat the predicate as failing, removing the guarded alternative.
1002    AssumeFalse,
1003    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
1004    /// coordinate that recognition evaluated.
1005    Error,
1006}
1007
1008/// Resolves a predicate coordinate that neither a translated table entry nor a
1009/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
1010///
1011/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
1012/// so the parse entry can surface every unresolved coordinate afterwards. Both
1013/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
1014/// funnel through here so a missing implementation is never silently coerced
1015/// to a boolean (design goal G1: never silently mis-parse).
1016fn apply_unknown_predicate_policy(
1017    policy: UnknownSemanticPolicy,
1018    rule_index: usize,
1019    pred_index: usize,
1020    hits: &mut Vec<(usize, usize)>,
1021) -> bool {
1022    match policy {
1023        UnknownSemanticPolicy::AssumeTrue => true,
1024        UnknownSemanticPolicy::AssumeFalse => false,
1025        UnknownSemanticPolicy::Error => {
1026            let coordinate = (rule_index, pred_index);
1027            if !hits.contains(&coordinate) {
1028                hits.push(coordinate);
1029            }
1030            false
1031        }
1032    }
1033}
1034
1035/// Interval-set of expected token types, displayable through a vocabulary —
1036/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
1037/// generated test actions.
1038#[derive(Clone, Debug, Eq, PartialEq)]
1039pub struct ExpectedTokenSet {
1040    symbols: BTreeSet<i32>,
1041}
1042
1043impl ExpectedTokenSet {
1044    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
1045    #[must_use]
1046    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
1047        expected_symbols_display(&self.symbols, vocabulary)
1048    }
1049}
1050
1051/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
1052///
1053/// The first syntax error aborts the parse instead of recovering. Generated
1054/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
1055#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1056pub struct BailErrorStrategy;
1057
1058impl BailErrorStrategy {
1059    #[must_use]
1060    pub const fn new() -> Self {
1061        Self
1062    }
1063}
1064
1065/// Prediction strategy requested by generated parser harnesses.
1066#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1067pub enum PredictionMode {
1068    /// Prefer the clean full-context outcome when alternatives reach the same
1069    /// input position.
1070    Ll,
1071    /// Preserve SLL's first-viable alternative bias at a decision, even when a
1072    /// later full-context alternative could avoid recovery.
1073    Sll,
1074    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
1075    LlExactAmbigDetection,
1076}
1077
1078/// Integer argument metadata for a generated parser rule invocation.
1079///
1080/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
1081/// the generator records the rule-transition source state and the value that
1082/// should be visible to semantic predicates inside the callee.
1083#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1084pub struct ParserRuleArg {
1085    /// ATN state containing the rule transition that receives this argument.
1086    pub source_state: usize,
1087    /// Callee rule index for the transition.
1088    pub rule_index: usize,
1089    /// Literal fallback value to expose in the callee.
1090    pub value: i64,
1091    /// Whether the callee should inherit the caller's current integer argument.
1092    pub inherit_local: bool,
1093}
1094
1095/// Integer member mutation attached to an ATN action transition.
1096#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1097pub struct ParserMemberAction {
1098    /// ATN state containing the action transition.
1099    pub source_state: usize,
1100    /// Generator-assigned integer member id.
1101    pub member: usize,
1102    /// Delta applied when the action is reached on one speculative path.
1103    pub delta: i64,
1104}
1105
1106/// Integer return-value assignment attached to an ATN action transition.
1107///
1108/// Generated parsers use this metadata when target actions assign a simple
1109/// return field such as `$y=1000;`. The interpreter applies it while selecting
1110/// the recognized path so the finished parse tree can answer later
1111/// `$label.y` action templates.
1112#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1113pub struct ParserReturnAction {
1114    /// ATN state containing the action transition.
1115    pub source_state: usize,
1116    /// Rule index recorded by the serialized action transition.
1117    pub rule_index: usize,
1118    /// Return-field name as it appears in the grammar.
1119    pub name: &'static str,
1120    /// Literal integer value assigned by the action.
1121    pub value: i64,
1122}
1123
1124impl ParserMemberAction {
1125    /// Lowers this speculative member mutation into a `SemIR` action.
1126    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1127        let delta = ir.expr(PExpr::Int(self.delta));
1128        ParserSemanticAction {
1129            source_state: self.source_state,
1130            rule_index: usize::MAX,
1131            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
1132            speculative: true,
1133        }
1134    }
1135}
1136
1137impl ParserReturnAction {
1138    /// Lowers this committed return-value assignment into a `SemIR` action.
1139    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1140        let name = ir.intern(self.name);
1141        let value = ir.expr(PExpr::Int(self.value));
1142        ParserSemanticAction {
1143            source_state: self.source_state,
1144            rule_index: self.rule_index,
1145            stmt: ir.stmt(AStmt::SetReturn(name, value)),
1146            speculative: false,
1147        }
1148    }
1149}
1150
1151/// Parser predicate coordinate lowered into [`SemIr`].
1152#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1153pub struct ParserSemanticPredicate {
1154    /// Serialized rule index that owns this predicate.
1155    pub rule_index: usize,
1156    /// Predicate index inside the owning rule.
1157    pub pred_index: usize,
1158    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1159    pub expr: ExprId,
1160    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1161    pub failure_message: Option<&'static str>,
1162}
1163
1164/// Parser action coordinate lowered into [`SemIr`].
1165#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1166pub struct ParserSemanticAction {
1167    /// ATN state containing the action transition.
1168    pub source_state: usize,
1169    /// Serialized rule index recorded by the action transition.
1170    pub rule_index: usize,
1171    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1172    pub stmt: StmtId,
1173    /// Whether this action may run on speculative recognition paths.
1174    pub speculative: bool,
1175}
1176
1177/// Data-driven semantic tables emitted by generated parsers.
1178///
1179/// This is the runtime representation for issue #9's `SemIR` path. Existing
1180/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1181/// remain accepted as deprecated adapters for generated code produced before
1182/// this table existed.
1183#[derive(Clone, Debug, Default, Eq, PartialEq)]
1184pub struct ParserSemantics {
1185    pub ir: SemIr,
1186    pub predicates: Vec<ParserSemanticPredicate>,
1187    pub actions: Vec<ParserSemanticAction>,
1188}
1189
1190/// Optional generated-runtime metadata for metadata-driven parser execution.
1191#[derive(Clone, Copy, Debug, Default)]
1192pub struct ParserRuntimeOptions<'a> {
1193    /// Rule indexes whose `@init` actions should be replayed.
1194    pub init_action_rules: &'a [usize],
1195    /// Whether generated parse-tree contexts should retain alternative numbers.
1196    pub track_alt_numbers: bool,
1197    /// Whether generated typed contexts should retain private dispatch alternatives.
1198    ///
1199    /// Unlike `track_alt_numbers`, this metadata does not affect the public
1200    /// alternative number or parse-tree rendering.
1201    #[doc(hidden)]
1202    pub track_context_alt_numbers: bool,
1203    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1204    pub predicates: &'a [(usize, usize, ParserPredicate)],
1205    /// `SemIR` predicate/action table emitted by newer generated parsers.
1206    pub semantics: Option<&'a ParserSemantics>,
1207    /// Rule-call integer argument table keyed by ATN source state.
1208    pub rule_args: &'a [ParserRuleArg],
1209    /// Integer member mutations keyed by ATN action source state.
1210    pub member_actions: &'a [ParserMemberAction],
1211    /// Integer return assignments keyed by ATN action source state.
1212    pub return_actions: &'a [ParserReturnAction],
1213    /// How to evaluate semantic predicate coordinates absent from
1214    /// `predicates`.
1215    pub unknown_predicate_policy: UnknownSemanticPolicy,
1216}
1217
1218pub trait Parser: Recognizer {
1219    /// Reports whether generated parser rules should build parse-tree nodes
1220    /// while recognizing input.
1221    fn build_parse_trees(&self) -> bool;
1222
1223    /// Enables or disables parse-tree construction for subsequent rule calls.
1224    fn set_build_parse_trees(&mut self, build: bool);
1225
1226    /// Returns the number of parser syntax errors recorded by committed parse
1227    /// paths so far.
1228    fn number_of_syntax_errors(&self) -> usize {
1229        0
1230    }
1231
1232    /// Reports whether prediction diagnostic-listener messages are emitted
1233    /// during parser ATN recognition.
1234    fn report_diagnostic_errors(&self) -> bool {
1235        false
1236    }
1237
1238    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1239    /// rule calls.
1240    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1241
1242    /// Reports the prediction strategy used when selecting among alternatives.
1243    fn prediction_mode(&self) -> PredictionMode {
1244        PredictionMode::Ll
1245    }
1246
1247    /// Sets the prediction strategy for subsequent rule calls.
1248    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1249
1250    /// Maximum rule-nesting depth accepted before the parse aborts, or `None`
1251    /// for unlimited (the default).
1252    fn max_rule_depth(&self) -> Option<usize> {
1253        None
1254    }
1255
1256    /// Bounds the rule-nesting depth for subsequent rule calls.
1257    ///
1258    /// Deeply nested input is parsed safely regardless (rule recursion grows
1259    /// onto a segmented stack), but each nesting level still costs CPU and
1260    /// tree memory. Callers parsing untrusted input can cap that work: when
1261    /// the limit is exceeded the parse stops with a positioned syntax error
1262    /// instead of consuming unbounded resources. The measure counts rule
1263    /// frames plus left-recursive operator expansions, matching what an
1264    /// upstream-ANTLR rule-entry listener observes.
1265    ///
1266    /// The cap is enforced by generated recursive-descent rule bodies. When
1267    /// one is set, generated dispatch routes ATN-preferred rules through
1268    /// their generated bodies too, trading that fast path for enforcement.
1269    /// Rules the generator emitted no body for (interpreter-only fallback)
1270    /// do not check the cap.
1271    fn set_max_rule_depth(&mut self, _depth: Option<usize>) {}
1272
1273    /// Registers a listener for committed rule enter/exit events during
1274    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
1275    /// the delivery contract. The default implementation drops the listener;
1276    /// [`BaseParser`] and generated parsers deliver events.
1277    fn add_parse_listener(&mut self, _listener: Box<dyn ParseListener>) {}
1278
1279    /// Removes every registered parse listener and returns them, dropping
1280    /// any sticky abort a removed listener had requested.
1281    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
1282        Vec::new()
1283    }
1284}
1285
1286#[derive(Debug)]
1287struct LeftRecursiveCallerOverlap {
1288    atn_key: SharedAtnCacheKey,
1289    state_number: usize,
1290    symbol: i32,
1291    context_version: usize,
1292    overlaps: bool,
1293}
1294
1295const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1296
1297#[derive(Debug)]
1298pub struct BaseParser<S, H = NoSemanticHooks> {
1299    input: CommonTokenStream<S>,
1300    tree: ParseTreeStorage,
1301    data: RecognizerData,
1302    semantic_hooks: H,
1303    decision_override_generation: usize,
1304    build_parse_trees: bool,
1305    syntax_errors: usize,
1306    report_diagnostic_errors: bool,
1307    prediction_mode: PredictionMode,
1308    prediction_diagnostics: Vec<ParserDiagnostic>,
1309    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1310    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1311    generated_sync_expected: Option<TokenBitSet>,
1312    generated_recovery_error_index: Option<usize>,
1313    generated_recovery_error_states: BTreeSet<isize>,
1314    int_members: MemberEnv,
1315    rule_context_stack: Vec<RuleContextFrame>,
1316    rule_context_version: usize,
1317    left_recursive_caller_overlap_cache:
1318        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1319    pending_invoking_states: Vec<isize>,
1320    precedence_stack: Vec<i32>,
1321    /// Predicate side effects are observable in a few target-template tests;
1322    /// speculative recognition may revisit the same coordinate, so replay it
1323    /// once per parser instance.
1324    invoked_predicates: Vec<(usize, usize)>,
1325    /// Bail error strategy: the first syntax error aborts the parse instead of
1326    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1327    /// through `set_error_handler(BailErrorStrategy::new())`.
1328    bail_on_error: bool,
1329    /// Parse listeners receiving committed rule enter/exit events during
1330    /// recognition (ANTLR's `addParseListener`). Empty in the default
1331    /// configuration, and every dispatch site is gated on emptiness so the
1332    /// unused feature costs one predictable branch per rule boundary.
1333    parse_listeners: Vec<ParseListenerSlot>,
1334    /// Sticky abort requested by a parse listener's `enter_every_rule`.
1335    /// Mirrors `rule_depth_error`: rule-level recovery absorbs the error like
1336    /// any rule failure, so the flag stays set until the top-level entry
1337    /// drains it and fails the parse.
1338    parse_listener_abort: Option<AntlrError>,
1339    /// Optional cap on rule-nesting depth for adversarial-input hardening.
1340    /// `None` (default) parses unbounded nesting; `Some(n)` aborts the parse
1341    /// with a positioned syntax error once `n` rule frames are exceeded.
1342    max_rule_depth: Option<usize>,
1343    /// Sticky depth-cap violation. Rule-level recovery would otherwise absorb
1344    /// the error and keep parsing; once set, every subsequent rule entry fails
1345    /// immediately and the top-level entry returns this error even when
1346    /// recovery produced a tree.
1347    rule_depth_error: Option<AntlrError>,
1348    /// Left-recursive expansions currently deepening the parse tree. Each
1349    /// operator iteration wraps the previous context one level deeper without
1350    /// pushing a rule frame, so the depth cap must count these separately —
1351    /// upstream ANTLR fires a rule-entry listener event for exactly this case
1352    /// (`Parser.pushNewRecursionContext` → `triggerEnterRuleEvent`).
1353    recursion_expansions: usize,
1354    /// Per-invocation snapshots of [`Self::recursion_expansions`], pushed by
1355    /// `enter_recursion_rule` and restored by `unroll_recursion_context`, so a
1356    /// finished left-recursive rule releases the depth its expansions added.
1357    recursion_expansion_marks: Vec<usize>,
1358    /// How to evaluate predicate coordinates missing from the active
1359    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1360    unknown_predicate_policy: UnknownSemanticPolicy,
1361    /// Unknown predicate coordinates evaluated by the current parse, recorded
1362    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1363    unknown_predicate_hits: Vec<(usize, usize)>,
1364    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1365    /// that no hook handled, recorded so a generated `hook`/error-disposed
1366    /// action fails loud instead of being silently dropped. Keyed by
1367    /// `(rule_index, source_state)`.
1368    unhandled_action_hits: Vec<(usize, usize)>,
1369    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1370    /// hot rule-transition checks to a vector lookup after the first visit
1371    /// while the thread-local shared ATN cache still owns the cross-parse
1372    /// computed value.
1373    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1374    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1375    /// epsilon-reachable consuming transition and shows up as a hot loop in
1376    /// `next_recovery_context` and recovery diagnostics on long inputs.
1377    /// Keying on `state_number` and sharing the result through `Rc` removes
1378    /// repeated DFS plus per-call `BTreeSet` allocations.
1379    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1380    /// Same expected-symbol cache as a bitset for generated parser sync.
1381    /// Successful parses only need `contains` and union; keeping that path out
1382    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1383    /// check and defers deterministic formatting to diagnostics.
1384    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1385    /// Per-state cache for whether a return state can finish its owning rule
1386    /// without consuming more input. Generated-parser sync uses this to walk
1387    /// parent prediction contexts for nullable exits without paying repeated
1388    /// epsilon-closure searches on every loop or optional decision.
1389    rule_stop_reach_cache: Vec<Option<bool>>,
1390    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1391    /// threads the same epsilon-recovery context through hundreds of follow
1392    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1393    /// reference bump and lets the memo key hash by pointer.
1394    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1395    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1396    /// touch a given decision state still pay no upfront cost; once cached,
1397    /// the recognizer prunes alternatives whose look-1 cannot accept the
1398    /// current lookahead, letting common SLL decisions reduce to a single
1399    /// transition walk instead of a full speculative fan-out.
1400    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1401    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1402    /// Each multi-trans visit asks "given this decision state and this
1403    /// lookahead token, which alt do I commit to?" Hitting this cache
1404    /// turns the question into a hashmap probe instead of re-scanning
1405    /// the decision's per-transition FIRST sets every visit.
1406    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1407    /// Predicate results shared by the fast recognizer's clean and recovery
1408    /// attempts. The eligible fast path keeps every runtime-provided input
1409    /// fixed, and custom predicate hooks are required to be replay-safe.
1410    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1411    /// Cache for whether an ATN state can reach itself without consuming
1412    /// input. Only those states need the recursive recognizer's
1413    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1414    /// grammar-static cache survive parser resets without reusing state
1415    /// coordinates after the parser is driven against a different ATN.
1416    empty_cycle_cache: Vec<Option<bool>>,
1417    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1418    /// Probe state for deciding whether clean-pass memo entries are worth
1419    /// storing for the current parse.
1420    clean_memo_mode: CleanMemoMode,
1421    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1422    clean_memo_probe_samples: usize,
1423    clean_memo_probe_repeats: usize,
1424    clean_memo_sparse_samples: usize,
1425    /// Reusable cycle and memo storage for one top-level fast recognition.
1426    fast_recognize_scratch: FastRecognizeTopScratch,
1427    /// Reusable direct-index/hash storage for clean speculative endpoints.
1428    fast_outcome_dedup: FastOutcomeDedupScratch,
1429    /// Empty recovery-symbols singleton used as the default at rule entry and
1430    /// after token consumption.
1431    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1432    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1433    /// prefilter trims speculative rule calls whose called rule cannot
1434    /// match the current lookahead, but it also bypasses single-token
1435    /// insertion / deletion recovery that ANTLR runs at the rule's first
1436    /// consuming transition. `parse_atn_rule` flips this off and retries
1437    /// when the first pass produces no clean outcome so the runtime can
1438    /// repair inputs the reference parser would have repaired.
1439    fast_first_set_prefilter: bool,
1440    /// Whether the fast recognizer should explore parser error-recovery paths.
1441    /// Public rule parsing starts with this disabled for the common valid-input
1442    /// path and enables it only for the retry that needs ANTLR-style repairs.
1443    fast_recovery_enabled: bool,
1444    /// Whether the fast recognizer should record terminal-token nodes while
1445    /// speculating. Clean valid-input parsing can reconstruct terminals from
1446    /// selected rule spans after recognition, avoiding many speculative
1447    /// nodes that are thrown away with losing paths.
1448    fast_token_nodes_enabled: bool,
1449    /// Whether fast recognition should retain private/public rule alternatives
1450    /// in deferred tree metadata.
1451    fast_track_alt_numbers: bool,
1452    /// Parser-owned append-only storage for speculative recognition output.
1453    /// Each public interpreted-rule entry clears lengths while retaining
1454    /// bounded backing capacities for parser reuse.
1455    recognition_arena: RecognitionArena,
1456    last_recognition_arena_root: NodeSeqId,
1457    last_recognition_arena_diagnostics: DiagnosticSeqId,
1458}
1459
1460/// Rollback marker for speculative generated parser paths.
1461#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1462pub struct GeneratedDiagnosticsCheckpoint {
1463    diagnostics_len: usize,
1464    syntax_errors: usize,
1465    tree: ParseTreeCheckpoint,
1466}
1467
1468/// Storage and reachability counters for the most recent interpreted-rule
1469/// recognition arena.
1470#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1471pub struct RecognitionArenaStats {
1472    pub total_nodes: usize,
1473    pub live_nodes: usize,
1474    pub dead_nodes: usize,
1475    pub node_capacity: usize,
1476    pub total_links: usize,
1477    pub live_links: usize,
1478    pub dead_links: usize,
1479    pub link_capacity: usize,
1480    pub total_extras: usize,
1481    pub live_extras: usize,
1482    pub dead_extras: usize,
1483    pub extra_capacity: usize,
1484}
1485
1486#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1487struct RuleContextFrame {
1488    rule_index: usize,
1489    invoking_state: isize,
1490}
1491
1492#[derive(Clone, Debug, Eq, PartialEq)]
1493struct RecognizeOutcome {
1494    index: usize,
1495    consumed_eof: bool,
1496    alt_number: usize,
1497    member_values: MemberEnv,
1498    return_values: BTreeMap<String, i64>,
1499    diagnostics: DiagnosticSeqId,
1500    decisions: Vec<usize>,
1501    actions: Vec<ParserAction>,
1502    nodes: NodeSeqId,
1503}
1504
1505#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1506struct FastRecognizeOutcome {
1507    index: usize,
1508    consumed_eof: bool,
1509    diagnostics: DiagnosticSeqId,
1510    deferred_nodes: FastDeferredNodeId,
1511    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1512    /// Copying an outcome copies this compact ID; prepending appends one
1513    /// `SeqLink` without allocating an individual node or list tail.
1514    nodes: NodeSeqId,
1515}
1516
1517#[derive(Debug, Default)]
1518struct FastRecognizeTopScratch {
1519    visiting: FxHashSet<FastRecognizeKey>,
1520    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1521}
1522
1523impl FastRecognizeTopScratch {
1524    fn prepare(&mut self, memo_capacity: usize) {
1525        self.visiting.clear();
1526        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1527        self.memo.clear();
1528        self.memo.reserve(memo_capacity);
1529    }
1530
1531    fn release_oversized_memo(&mut self) {
1532        self.memo.clear();
1533        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1534            self.memo = FxHashMap::default();
1535        }
1536    }
1537}
1538
1539fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1540    buffered_tokens.saturating_mul(8).clamp(
1541        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1542        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1543    )
1544}
1545
1546#[derive(Debug, Default)]
1547struct FastOutcomeDedupScratch {
1548    dense_words: Vec<u64>,
1549    touched_dense_words: Vec<u32>,
1550    sparse_keys: FxHashSet<(usize, bool)>,
1551}
1552
1553/// Handle into the parser-owned deferred tree rope.
1554///
1555/// The sentinel keeps outcomes and repetition paths compact without an
1556/// `Option` discriminant or per-node reference counting.
1557#[repr(transparent)]
1558#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1559struct FastDeferredNodeId(u32);
1560
1561impl FastDeferredNodeId {
1562    const EMPTY: Self = Self(u32::MAX);
1563
1564    const fn is_empty(self) -> bool {
1565        self.0 == Self::EMPTY.0
1566    }
1567}
1568
1569impl Default for FastDeferredNodeId {
1570    fn default() -> Self {
1571        Self::EMPTY
1572    }
1573}
1574
1575#[repr(transparent)]
1576#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1577struct FastDeferredRuleId(u32);
1578
1579/// One immutable deferred-tree rope record in `RecognitionArena`.
1580#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1581enum FastDeferredNode {
1582    Fragment(NodeSeqId),
1583    Rule(FastDeferredRuleId),
1584    Alternative(u32),
1585    LeftRecursiveBoundary {
1586        rule_index: u32,
1587    },
1588    Concat {
1589        prefix: FastDeferredNodeId,
1590        suffix: FastDeferredNodeId,
1591    },
1592}
1593
1594#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1595struct FastDeferredRule {
1596    rule_index: u32,
1597    invoking_state: i32,
1598    start_index: u32,
1599    stop_index: Option<u32>,
1600    deferred_children: FastDeferredNodeId,
1601    children: NodeSeqId,
1602}
1603
1604#[repr(transparent)]
1605#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1606struct RecognizedNodeId(u32);
1607
1608#[repr(transparent)]
1609#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1610struct NodeSeqId(u32);
1611
1612impl NodeSeqId {
1613    const EMPTY: Self = Self(u32::MAX);
1614
1615    const fn is_empty(self) -> bool {
1616        self.0 == Self::EMPTY.0
1617    }
1618}
1619
1620impl Default for NodeSeqId {
1621    fn default() -> Self {
1622        Self::EMPTY
1623    }
1624}
1625
1626#[repr(transparent)]
1627#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1628struct DiagnosticSeqId(u32);
1629
1630impl DiagnosticSeqId {
1631    const EMPTY: Self = Self(u32::MAX);
1632
1633    const fn is_empty(self) -> bool {
1634        self.0 == Self::EMPTY.0
1635    }
1636}
1637
1638impl Default for DiagnosticSeqId {
1639    fn default() -> Self {
1640        Self::EMPTY
1641    }
1642}
1643
1644#[repr(transparent)]
1645#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1646struct RecognitionExtraId(u32);
1647
1648#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1649struct SeqLink {
1650    head: RecognizedNodeId,
1651    tail: NodeSeqId,
1652}
1653
1654#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1655struct DiagnosticLink {
1656    head: RecognitionExtraId,
1657    tail: DiagnosticSeqId,
1658}
1659
1660struct ArenaRuleSpec {
1661    rule_index: usize,
1662    invoking_state: isize,
1663    alt_number: usize,
1664    start_index: usize,
1665    stop_index: Option<usize>,
1666    return_values: BTreeMap<String, i64>,
1667    children: NodeSeqId,
1668}
1669
1670/// Compact speculative node record. Common records contain only IDs and
1671/// scalars; missing-token text and generated return values live in `extras`.
1672#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1673enum ArenaRecognizedNode {
1674    Token {
1675        token: TokenId,
1676    },
1677    ErrorToken {
1678        token: TokenId,
1679    },
1680    MissingToken {
1681        extra: RecognitionExtraId,
1682    },
1683    Rule {
1684        rule_index: u32,
1685        invoking_state: i32,
1686        alt_number: u32,
1687        start_index: u32,
1688        stop_index: Option<u32>,
1689        return_values: Option<RecognitionExtraId>,
1690        children: NodeSeqId,
1691    },
1692    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1693    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1694    /// public rule entry hands the tree to the caller.
1695    LeftRecursiveBoundary {
1696        rule_index: u32,
1697        alt_number: u32,
1698    },
1699}
1700
1701#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1702enum RecognitionExtra {
1703    MissingToken {
1704        token_type: i32,
1705        at_index: u32,
1706        text: String,
1707    },
1708    ReturnValues(BTreeMap<String, i64>),
1709    Diagnostic(ParserDiagnostic),
1710}
1711
1712#[derive(Debug, Default)]
1713struct RecognitionArena {
1714    nodes: Vec<ArenaRecognizedNode>,
1715    seq_links: Vec<SeqLink>,
1716    diagnostic_links: Vec<DiagnosticLink>,
1717    extras: Vec<RecognitionExtra>,
1718    deferred_nodes: Vec<FastDeferredNode>,
1719    deferred_rules: Vec<FastDeferredRule>,
1720}
1721
1722// Preserve normal parser reuse while preventing one pathological parse from
1723// pinning an arbitrarily large arena for the parser's remaining lifetime.
1724const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1725const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1726const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1727const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1728const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1729const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1730
1731impl RecognitionArena {
1732    fn reset(&mut self) {
1733        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1734        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1735        reset_arena_vec(
1736            &mut self.diagnostic_links,
1737            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1738        );
1739        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1740        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1741        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1742    }
1743
1744    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1745        let id = RecognizedNodeId(
1746            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1747        );
1748        self.nodes.push(node);
1749        id
1750    }
1751
1752    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1753        let id = RecognitionExtraId(
1754            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1755        );
1756        self.extras.push(extra);
1757        id
1758    }
1759
1760    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1761        let id = NodeSeqId(
1762            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1763        );
1764        self.seq_links.push(SeqLink { head, tail });
1765        id
1766    }
1767
1768    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1769        let id = FastDeferredNodeId(
1770            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
1771        );
1772        self.deferred_nodes.push(node);
1773        id
1774    }
1775
1776    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
1777        let id = FastDeferredRuleId(
1778            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
1779        );
1780        self.deferred_rules.push(rule);
1781        id
1782    }
1783
1784    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
1785        if nodes.is_empty() {
1786            FastDeferredNodeId::EMPTY
1787        } else {
1788            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
1789        }
1790    }
1791
1792    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
1793        let rule = self.push_deferred_rule(rule);
1794        self.push_deferred_node(FastDeferredNode::Rule(rule))
1795    }
1796
1797    fn deferred_alternative(&mut self, alt_number: usize) -> FastDeferredNodeId {
1798        self.push_deferred_node(FastDeferredNode::Alternative(
1799            u32::try_from(alt_number).expect("alternative number fits in u32"),
1800        ))
1801    }
1802
1803    fn deferred_left_recursive_boundary(&mut self, rule_index: usize) -> FastDeferredNodeId {
1804        self.push_deferred_node(FastDeferredNode::LeftRecursiveBoundary {
1805            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
1806        })
1807    }
1808
1809    fn concat_deferred_nodes(
1810        &mut self,
1811        prefix: FastDeferredNodeId,
1812        suffix: FastDeferredNodeId,
1813    ) -> FastDeferredNodeId {
1814        if prefix.is_empty() {
1815            return suffix;
1816        }
1817        if suffix.is_empty() {
1818            return prefix;
1819        }
1820        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
1821    }
1822
1823    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
1824        self.deferred_nodes[id.0 as usize]
1825    }
1826
1827    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
1828        self.deferred_rules[id.0 as usize]
1829    }
1830
1831    fn prepend_diagnostic(
1832        &mut self,
1833        tail: DiagnosticSeqId,
1834        diagnostic: ParserDiagnostic,
1835    ) -> DiagnosticSeqId {
1836        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
1837        self.prepend_diagnostic_id(tail, head)
1838    }
1839
1840    fn prepend_diagnostic_id(
1841        &mut self,
1842        tail: DiagnosticSeqId,
1843        head: RecognitionExtraId,
1844    ) -> DiagnosticSeqId {
1845        let id = DiagnosticSeqId(
1846            u32::try_from(self.diagnostic_links.len())
1847                .expect("diagnostic sequence arena fits in u32"),
1848        );
1849        self.diagnostic_links.push(DiagnosticLink { head, tail });
1850        id
1851    }
1852
1853    fn concat_diagnostics(
1854        &mut self,
1855        prefix: DiagnosticSeqId,
1856        mut suffix: DiagnosticSeqId,
1857    ) -> DiagnosticSeqId {
1858        if prefix.is_empty() {
1859            return suffix;
1860        }
1861        if suffix.is_empty() {
1862            return prefix;
1863        }
1864        let mut reversed = DiagnosticSeqId::EMPTY;
1865        let mut cursor = prefix;
1866        while let Some(link) = self.diagnostic_link(cursor) {
1867            reversed = self.prepend_diagnostic_id(reversed, link.head);
1868            cursor = link.tail;
1869        }
1870        while let Some(link) = self.diagnostic_link(reversed) {
1871            suffix = self.prepend_diagnostic_id(suffix, link.head);
1872            reversed = link.tail;
1873        }
1874        suffix
1875    }
1876
1877    #[cfg(test)]
1878    fn diagnostic_sequence(
1879        &mut self,
1880        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
1881    ) -> DiagnosticSeqId {
1882        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
1883        let mut sequence = DiagnosticSeqId::EMPTY;
1884        for diagnostic in diagnostics.into_iter().rev() {
1885            sequence = self.prepend_diagnostic(sequence, diagnostic);
1886        }
1887        sequence
1888    }
1889
1890    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
1891        self.nodes[id.0 as usize]
1892    }
1893
1894    fn set_boundary_alt_number(&mut self, id: RecognizedNodeId, alt_number: u32) {
1895        let ArenaRecognizedNode::LeftRecursiveBoundary {
1896            alt_number: stored, ..
1897        } = &mut self.nodes[id.0 as usize]
1898        else {
1899            unreachable!("deferred boundary must materialize as a boundary node");
1900        };
1901        *stored = alt_number;
1902    }
1903
1904    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
1905        &self.extras[id.0 as usize]
1906    }
1907
1908    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
1909        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
1910    }
1911
1912    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
1913        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
1914    }
1915
1916    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
1917        NodeSeqIter {
1918            arena: self,
1919            cursor: sequence,
1920        }
1921    }
1922
1923    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
1924        DiagnosticSeqIter {
1925            arena: self,
1926            cursor: sequence,
1927        }
1928    }
1929
1930    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
1931        self.diagnostics(sequence).count()
1932    }
1933
1934    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
1935        self.diagnostics(sequence)
1936            .filter(|diagnostic| {
1937                diagnostic.message.starts_with("mismatched input ")
1938                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
1939            })
1940            .count()
1941    }
1942
1943    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
1944        self.diagnostics(left).cmp(self.diagnostics(right))
1945    }
1946
1947    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
1948        self.iter(sequence).count()
1949    }
1950
1951    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
1952        self.iter(sequence).any(|node| match self.node(node) {
1953            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
1954            ArenaRecognizedNode::Rule { children, .. } => {
1955                self.sequence_has_left_recursive_boundary(children)
1956            }
1957            ArenaRecognizedNode::Token { .. }
1958            | ArenaRecognizedNode::ErrorToken { .. }
1959            | ArenaRecognizedNode::MissingToken { .. } => false,
1960        })
1961    }
1962
1963    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
1964        self.iter(sequence).any(|node| {
1965            matches!(
1966                self.node(node),
1967                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
1968            )
1969        })
1970    }
1971
1972    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
1973        self.iter(sequence).any(|node| {
1974            matches!(
1975                self.node(node),
1976                ArenaRecognizedNode::Token { .. }
1977                    | ArenaRecognizedNode::ErrorToken { .. }
1978                    | ArenaRecognizedNode::MissingToken { .. }
1979            )
1980        })
1981    }
1982
1983    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
1984        match self.node(node) {
1985            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
1986                Some(token.index())
1987            }
1988            ArenaRecognizedNode::MissingToken { extra } => {
1989                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
1990                    unreachable!("missing-token node must reference missing-token extra");
1991                };
1992                Some(*at_index as usize)
1993            }
1994            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
1995            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
1996        }
1997    }
1998
1999    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
2000        match self.node(node) {
2001            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
2002                Some(token.index())
2003            }
2004            ArenaRecognizedNode::MissingToken { extra } => {
2005                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
2006                    unreachable!("missing-token node must reference missing-token extra");
2007                };
2008                (*at_index as usize).checked_sub(1)
2009            }
2010            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
2011            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
2012        }
2013    }
2014
2015    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
2016        let start = self.node_start_index(node)?;
2017        let stop = self.node_stop_index(node);
2018        Some((start, stop))
2019    }
2020
2021    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
2022        self.iter(sequence)
2023            .find_map(|node| self.node_start_index(node))
2024    }
2025
2026    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
2027        let mut stop = None;
2028        for node in self.iter(sequence) {
2029            if let Some(index) = self.node_stop_index(node) {
2030                stop = Some(index);
2031            }
2032        }
2033        stop
2034    }
2035
2036    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
2037        self.iter(sequence)
2038            .any(|node| self.node_needs_stable_tie(node))
2039    }
2040
2041    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
2042        match self.node(node) {
2043            ArenaRecognizedNode::Token { .. }
2044            | ArenaRecognizedNode::ErrorToken { .. }
2045            | ArenaRecognizedNode::MissingToken { .. } => false,
2046            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
2047            ArenaRecognizedNode::Rule {
2048                rule_index,
2049                children,
2050                ..
2051            } => self.iter(children).any(|child| {
2052                matches!(
2053                    self.node(child),
2054                    ArenaRecognizedNode::Rule {
2055                        rule_index: child_rule,
2056                        ..
2057                    } if child_rule == rule_index
2058                ) || self.node_needs_stable_tie(child)
2059            }),
2060        }
2061    }
2062
2063    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
2064        loop {
2065            match (self.link(left), self.link(right)) {
2066                (Some(left_link), Some(right_link)) => {
2067                    let order = self.compare_nodes(left_link.head, right_link.head);
2068                    if order != Ordering::Equal {
2069                        return order;
2070                    }
2071                    left = left_link.tail;
2072                    right = right_link.tail;
2073                }
2074                (None, None) => return Ordering::Equal,
2075                (None, Some(_)) => return Ordering::Less,
2076                (Some(_), None) => return Ordering::Greater,
2077            }
2078        }
2079    }
2080
2081    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
2082        let left = self.node(left);
2083        let right = self.node(right);
2084        match (left, right) {
2085            (
2086                ArenaRecognizedNode::Token { token: left },
2087                ArenaRecognizedNode::Token { token: right },
2088            )
2089            | (
2090                ArenaRecognizedNode::ErrorToken { token: left },
2091                ArenaRecognizedNode::ErrorToken { token: right },
2092            ) => left.cmp(&right),
2093            (
2094                ArenaRecognizedNode::MissingToken { extra: left },
2095                ArenaRecognizedNode::MissingToken { extra: right },
2096            ) => self.extra(left).cmp(self.extra(right)),
2097            (
2098                ArenaRecognizedNode::Rule {
2099                    rule_index: left_rule,
2100                    invoking_state: left_invoking,
2101                    alt_number: left_alt,
2102                    start_index: left_start,
2103                    stop_index: left_stop,
2104                    return_values: left_returns,
2105                    children: left_children,
2106                },
2107                ArenaRecognizedNode::Rule {
2108                    rule_index: right_rule,
2109                    invoking_state: right_invoking,
2110                    alt_number: right_alt,
2111                    start_index: right_start,
2112                    stop_index: right_stop,
2113                    return_values: right_returns,
2114                    children: right_children,
2115                },
2116            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
2117                .cmp(&(
2118                    right_rule,
2119                    right_invoking,
2120                    right_alt,
2121                    right_start,
2122                    right_stop,
2123                ))
2124                .then_with(|| {
2125                    left_returns
2126                        .map(|id| self.extra(id))
2127                        .cmp(&right_returns.map(|id| self.extra(id)))
2128                })
2129                .then_with(|| self.compare_sequences(left_children, right_children)),
2130            (
2131                ArenaRecognizedNode::LeftRecursiveBoundary {
2132                    rule_index: left_rule,
2133                    alt_number: left_alt,
2134                },
2135                ArenaRecognizedNode::LeftRecursiveBoundary {
2136                    rule_index: right_rule,
2137                    alt_number: right_alt,
2138                },
2139            ) => (left_rule, left_alt).cmp(&(right_rule, right_alt)),
2140            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
2141        }
2142    }
2143
2144    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
2145        let mut reversed = NodeSeqId::EMPTY;
2146        while let Some(link) = self.link(sequence) {
2147            reversed = self.prepend(reversed, link.head);
2148            sequence = link.tail;
2149        }
2150        reversed
2151    }
2152
2153    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
2154        if !self.sequence_has_direct_boundary(sequence) {
2155            return sequence;
2156        }
2157        let mut reversed = NodeSeqId::EMPTY;
2158        while let Some(link) = self.link(sequence) {
2159            match self.node(link.head) {
2160                ArenaRecognizedNode::LeftRecursiveBoundary {
2161                    rule_index,
2162                    alt_number,
2163                } => {
2164                    if !reversed.is_empty() {
2165                        let children = self.reverse_sequence(reversed);
2166                        let start_index = self.sequence_start_index(children).unwrap_or_default();
2167                        let stop_index = self.sequence_stop_index(children);
2168                        let rule = self.push_node(ArenaRecognizedNode::Rule {
2169                            rule_index,
2170                            invoking_state: -1,
2171                            alt_number,
2172                            start_index: u32::try_from(start_index)
2173                                .expect("left-recursive start index fits in u32"),
2174                            stop_index: stop_index.map(|index| {
2175                                u32::try_from(index).expect("left-recursive stop index fits in u32")
2176                            }),
2177                            return_values: None,
2178                            children,
2179                        });
2180                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
2181                    }
2182                }
2183                _ => {
2184                    reversed = self.prepend(reversed, link.head);
2185                }
2186            }
2187            sequence = link.tail;
2188        }
2189        self.reverse_sequence(reversed)
2190    }
2191
2192    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
2193        let mut live_nodes = vec![false; self.nodes.len()];
2194        let mut live_links = vec![false; self.seq_links.len()];
2195        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
2196        let mut live_extras = vec![false; self.extras.len()];
2197        let mut pending = vec![root];
2198        while let Some(mut sequence) = pending.pop() {
2199            while let Some(link) = self.link(sequence) {
2200                let link_index = sequence.0 as usize;
2201                if live_links[link_index] {
2202                    break;
2203                }
2204                live_links[link_index] = true;
2205                let node_index = link.head.0 as usize;
2206                if !live_nodes[node_index] {
2207                    live_nodes[node_index] = true;
2208                    match self.node(link.head) {
2209                        ArenaRecognizedNode::MissingToken { extra } => {
2210                            live_extras[extra.0 as usize] = true;
2211                        }
2212                        ArenaRecognizedNode::Rule {
2213                            return_values,
2214                            children,
2215                            ..
2216                        } => {
2217                            if let Some(extra) = return_values {
2218                                live_extras[extra.0 as usize] = true;
2219                            }
2220                            pending.push(children);
2221                        }
2222                        ArenaRecognizedNode::Token { .. }
2223                        | ArenaRecognizedNode::ErrorToken { .. }
2224                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
2225                    }
2226                }
2227                sequence = link.tail;
2228            }
2229        }
2230        let mut diagnostics = diagnostics;
2231        while let Some(link) = self.diagnostic_link(diagnostics) {
2232            let link_index = diagnostics.0 as usize;
2233            if live_diagnostic_links[link_index] {
2234                break;
2235            }
2236            live_diagnostic_links[link_index] = true;
2237            live_extras[link.head.0 as usize] = true;
2238            diagnostics = link.tail;
2239        }
2240        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
2241        let live_link_count = live_links.into_iter().filter(|live| *live).count()
2242            + live_diagnostic_links
2243                .into_iter()
2244                .filter(|live| *live)
2245                .count();
2246        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
2247        let total_links = self.seq_links.len() + self.diagnostic_links.len();
2248        RecognitionArenaStats {
2249            total_nodes: self.nodes.len(),
2250            live_nodes: live_node_count,
2251            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
2252            node_capacity: self.nodes.capacity(),
2253            total_links,
2254            live_links: live_link_count,
2255            dead_links: total_links.saturating_sub(live_link_count),
2256            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
2257            total_extras: self.extras.len(),
2258            live_extras: live_extra_count,
2259            dead_extras: self.extras.len().saturating_sub(live_extra_count),
2260            extra_capacity: self.extras.capacity(),
2261        }
2262    }
2263}
2264
2265fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
2266    if storage.capacity() > max_retained_capacity {
2267        *storage = Vec::new();
2268    } else {
2269        storage.clear();
2270    }
2271}
2272
2273const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2274    match node {
2275        ArenaRecognizedNode::Token { .. } => 0,
2276        ArenaRecognizedNode::ErrorToken { .. } => 1,
2277        ArenaRecognizedNode::MissingToken { .. } => 2,
2278        ArenaRecognizedNode::Rule { .. } => 3,
2279        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2280    }
2281}
2282
2283struct NodeSeqIter<'a> {
2284    arena: &'a RecognitionArena,
2285    cursor: NodeSeqId,
2286}
2287
2288impl Iterator for NodeSeqIter<'_> {
2289    type Item = RecognizedNodeId;
2290
2291    fn next(&mut self) -> Option<Self::Item> {
2292        let link = self.arena.link(self.cursor)?;
2293        self.cursor = link.tail;
2294        Some(link.head)
2295    }
2296}
2297
2298struct DiagnosticSeqIter<'a> {
2299    arena: &'a RecognitionArena,
2300    cursor: DiagnosticSeqId,
2301}
2302
2303impl<'a> Iterator for DiagnosticSeqIter<'a> {
2304    type Item = &'a ParserDiagnostic;
2305
2306    fn next(&mut self) -> Option<Self::Item> {
2307        let link = self.arena.diagnostic_link(self.cursor)?;
2308        self.cursor = link.tail;
2309        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2310            unreachable!("diagnostic link must reference diagnostic extra");
2311        };
2312        Some(diagnostic)
2313    }
2314}
2315
2316#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2317struct ParserDiagnostic {
2318    line: usize,
2319    column: usize,
2320    message: String,
2321    /// Token the diagnostic is anchored to, resolved to a view when the
2322    /// diagnostic is dispatched to error listeners. `None` when no token
2323    /// exists (synthetic positions, lexer-originated messages).
2324    offending: Option<TokenId>,
2325}
2326
2327#[derive(Clone, Debug, Default, Eq, PartialEq)]
2328struct ExpectedTokens {
2329    index: Option<usize>,
2330    symbols: BTreeSet<i32>,
2331    no_viable: Option<NoViableAlternative>,
2332}
2333
2334#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2335struct NoViableAlternative {
2336    start_index: usize,
2337    error_index: usize,
2338}
2339
2340impl ExpectedTokens {
2341    /// Records the expected symbols for the farthest token index reached by any
2342    /// failed ATN path.
2343    fn record_transition(
2344        &mut self,
2345        index: usize,
2346        transition: ParserTransition<'_>,
2347        max_token_type: i32,
2348    ) {
2349        let symbols = transition_expected_symbols(transition, max_token_type);
2350        match self.index {
2351            Some(current) if index < current => {}
2352            Some(current) if index == current => self.symbols.extend(symbols),
2353            _ => {
2354                self.index = Some(index);
2355                self.symbols = symbols;
2356            }
2357        }
2358    }
2359
2360    /// Records an ambiguous decision that failed after consuming a shared
2361    /// prefix, which ANTLR reports as `no viable alternative`.
2362    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2363        match self.no_viable {
2364            Some(current) if error_index < current.error_index => {}
2365            _ => {
2366                self.no_viable = Some(NoViableAlternative {
2367                    start_index,
2368                    error_index,
2369                });
2370            }
2371        }
2372    }
2373}
2374
2375/// Compact token-type set for parser-internal FIRST/lookahead caches.
2376///
2377/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2378/// but the hot recognizer path mostly needs `contains` and set union over
2379/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2380/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2381#[derive(Clone, Debug, Default, Eq, PartialEq)]
2382struct TokenBitSet {
2383    words: Vec<u64>,
2384}
2385
2386impl TokenBitSet {
2387    fn insert(&mut self, symbol: i32) {
2388        let Some(slot) = token_bit_slot(symbol) else {
2389            return;
2390        };
2391        let word = slot / u64::BITS as usize;
2392        if word >= self.words.len() {
2393            self.words.resize(word + 1, 0);
2394        }
2395        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2396    }
2397
2398    fn extend_range(&mut self, start: i32, stop: i32) {
2399        let (start, stop) = if start <= stop {
2400            (start, stop)
2401        } else {
2402            (stop, start)
2403        };
2404        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2405            self.insert(TOKEN_EOF);
2406        }
2407        let positive_start = start.max(1);
2408        if positive_start > stop {
2409            return;
2410        }
2411        let Some(start_slot) = token_bit_slot(positive_start) else {
2412            return;
2413        };
2414        let Some(stop_slot) = token_bit_slot(stop) else {
2415            return;
2416        };
2417        self.extend_slot_range(start_slot, stop_slot);
2418    }
2419
2420    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2421        if start_slot > stop_slot {
2422            return;
2423        }
2424        let start_word = start_slot / u64::BITS as usize;
2425        let stop_word = stop_slot / u64::BITS as usize;
2426        if stop_word >= self.words.len() {
2427            self.words.resize(stop_word + 1, 0);
2428        }
2429        let start_offset = start_slot % u64::BITS as usize;
2430        let stop_offset = stop_slot % u64::BITS as usize;
2431        if start_word == stop_word {
2432            self.words[start_word] |=
2433                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2434            return;
2435        }
2436        self.words[start_word] |= !0_u64 << start_offset;
2437        for word in &mut self.words[(start_word + 1)..stop_word] {
2438            *word = !0_u64;
2439        }
2440        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2441    }
2442
2443    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2444        for symbol in symbols {
2445            self.insert(symbol);
2446        }
2447    }
2448
2449    fn extend_from(&mut self, other: &Self) {
2450        if other.words.len() > self.words.len() {
2451            self.words.resize(other.words.len(), 0);
2452        }
2453        for (left, right) in self.words.iter_mut().zip(&other.words) {
2454            *left |= *right;
2455        }
2456    }
2457
2458    fn contains(&self, symbol: i32) -> bool {
2459        let Some(slot) = token_bit_slot(symbol) else {
2460            return false;
2461        };
2462        let word = slot / u64::BITS as usize;
2463        self.words
2464            .get(word)
2465            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2466    }
2467
2468    fn is_empty(&self) -> bool {
2469        self.words.iter().all(|word| *word == 0)
2470    }
2471
2472    fn symbols(&self) -> impl Iterator<Item = i32> + '_ {
2473        self.words
2474            .iter()
2475            .copied()
2476            .enumerate()
2477            .flat_map(|(word_index, mut bits)| {
2478                std::iter::from_fn(move || {
2479                    while bits != 0 {
2480                        let bit = bits.trailing_zeros() as usize;
2481                        bits &= bits - 1;
2482                        if let Some(symbol) =
2483                            token_bit_symbol(word_index * u64::BITS as usize + bit)
2484                        {
2485                            return Some(symbol);
2486                        }
2487                    }
2488                    None
2489                })
2490            })
2491    }
2492
2493    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2494        target.extend(self.symbols());
2495    }
2496
2497    fn to_btree_set(&self) -> BTreeSet<i32> {
2498        let mut out = BTreeSet::new();
2499        self.extend_btree_set(&mut out);
2500        out
2501    }
2502}
2503
2504fn token_bit_slot(symbol: i32) -> Option<usize> {
2505    if symbol == TOKEN_EOF {
2506        Some(0)
2507    } else if symbol > 0 {
2508        usize::try_from(symbol).ok()
2509    } else {
2510        None
2511    }
2512}
2513
2514fn token_bit_symbol(slot: usize) -> Option<i32> {
2515    if slot == 0 {
2516        Some(TOKEN_EOF)
2517    } else {
2518        i32::try_from(slot).ok()
2519    }
2520}
2521
2522/// Converts one consuming transition into the token types that would satisfy it
2523/// for diagnostic reporting.
2524fn transition_expected_symbols(
2525    transition: ParserTransition<'_>,
2526    max_token_type: i32,
2527) -> BTreeSet<i32> {
2528    let mut symbols = BTreeSet::new();
2529    match &transition.data() {
2530        Transition::Atom { label, .. } => {
2531            symbols.insert(*label);
2532        }
2533        Transition::Range { start, stop, .. } => {
2534            symbols.extend(*start..=*stop);
2535        }
2536        Transition::Set { set, .. } => {
2537            for (start, stop) in set.ranges() {
2538                symbols.extend(start..=stop);
2539            }
2540        }
2541        Transition::NotSet { set, .. } => {
2542            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2543        }
2544        Transition::Wildcard { .. } => {
2545            symbols.extend(1..=max_token_type);
2546        }
2547        Transition::Epsilon { .. }
2548        | Transition::Rule { .. }
2549        | Transition::Predicate { .. }
2550        | Transition::Action { .. }
2551        | Transition::Precedence { .. } => {}
2552    }
2553    symbols
2554}
2555
2556fn transition_expected_token_set(
2557    transition: ParserTransition<'_>,
2558    max_token_type: i32,
2559) -> TokenBitSet {
2560    let mut symbols = TokenBitSet::default();
2561    match &transition.data() {
2562        Transition::Atom { label, .. } => {
2563            symbols.insert(*label);
2564        }
2565        Transition::Range { start, stop, .. } => {
2566            symbols.extend_range(*start, *stop);
2567        }
2568        Transition::Set { set, .. } => {
2569            for (start, stop) in set.ranges() {
2570                symbols.extend_range(start, stop);
2571            }
2572        }
2573        Transition::NotSet { set, .. } => {
2574            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2575        }
2576        Transition::Wildcard { .. } => {
2577            symbols.extend_range(1, max_token_type);
2578        }
2579        Transition::Epsilon { .. }
2580        | Transition::Rule { .. }
2581        | Transition::Predicate { .. }
2582        | Transition::Action { .. }
2583        | Transition::Precedence { .. } => {}
2584    }
2585    symbols
2586}
2587
2588/// Returns the consuming-token expectations reachable from an ATN state through
2589/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2590/// and loop exits report the same expectation set ANTLR users see.
2591fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2592    let mut symbols = BTreeSet::new();
2593    let mut stack = vec![state_number];
2594    let mut visited = BTreeSet::new();
2595    while let Some(current) = stack.pop() {
2596        if !visited.insert(current) {
2597            continue;
2598        }
2599        let Some(state) = atn.state(current) else {
2600            continue;
2601        };
2602        for transition in &state.transitions() {
2603            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2604            if transition_symbols.is_empty() {
2605                if transition.is_epsilon() {
2606                    stack.push(transition.target());
2607                }
2608            } else {
2609                symbols.extend(transition_symbols);
2610            }
2611        }
2612    }
2613    symbols
2614}
2615
2616fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2617    let mut symbols = TokenBitSet::default();
2618    let mut stack = vec![state_number];
2619    let mut visited = BTreeSet::new();
2620    while let Some(current) = stack.pop() {
2621        if !visited.insert(current) {
2622            continue;
2623        }
2624        let Some(state) = atn.state(current) else {
2625            continue;
2626        };
2627        for transition in &state.transitions() {
2628            let transition_symbols =
2629                transition_expected_token_set(transition, atn.max_token_type());
2630            if transition_symbols.is_empty() {
2631                if transition.is_epsilon() {
2632                    stack.push(transition.target());
2633                }
2634            } else {
2635                symbols.extend_from(&transition_symbols);
2636            }
2637        }
2638    }
2639    symbols
2640}
2641
2642fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2643    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2644        return false;
2645    };
2646    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2647        return false;
2648    };
2649    epsilon_reaches_state(atn, state_number, stop_state)
2650}
2651
2652fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2653    let mut stack = vec![start];
2654    let mut visited = BTreeSet::new();
2655    while let Some(current) = stack.pop() {
2656        if current == target {
2657            return true;
2658        }
2659        if !visited.insert(current) {
2660            continue;
2661        }
2662        let Some(state) = atn.state(current) else {
2663            continue;
2664        };
2665        stack.extend(
2666            state
2667                .transitions()
2668                .iter()
2669                .filter(|transition| transition.is_epsilon())
2670                .map(ParserTransition::target),
2671        );
2672    }
2673    false
2674}
2675
2676/// FIRST set for a rule entry plus whether the rule is nullable.
2677///
2678/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2679/// a consuming transition or reaches the rule's stop state. Used by the fast
2680/// recognizer to skip rule alternatives whose first-consumed token cannot
2681/// possibly match the current lookahead.
2682#[derive(Clone, Debug, Default, Eq, PartialEq)]
2683struct FirstSet {
2684    symbols: TokenBitSet,
2685    nullable: bool,
2686}
2687
2688/// Per-parser cache of FIRST sets computed during recognition. The fast path
2689/// consults this on every speculative `Transition::Rule` encounter, so the
2690/// computation must amortize across all of those calls — the FIRST set is a
2691/// pure function of the ATN, not of the input position. Cached entries are
2692/// shared via `Rc` so the recognizer never deep-copies the underlying
2693/// `BTreeSet<i32>`.
2694type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2695
2696// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2697// and decision-lookahead entries are purely functions of the grammar's
2698// ATN, so caching across parses lets repeated parsing of the same grammar
2699// (the common case for a CLI tool or language server) avoid redoing the
2700// closure work. Generated parsers hand us a `&'static Atn` whose address
2701// is stable, which is what we hash on.
2702type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2703
2704#[derive(Debug, Default)]
2705struct LeftRecursiveOperatorLookahead {
2706    /// Operator alts whose token-prefix is fully matched by this one symbol
2707    /// (then only epsilons/actions remain before the recursive RHS call).
2708    /// Safe for one-token loop-enter fast path.
2709    single_token: TokenBitSet,
2710    /// Operator alts that start with this symbol but still require more tokens
2711    /// before the operand. Must not force enter from one-token lookahead when a
2712    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2713    /// has to weigh the exit alt as well.
2714    multi_token_prefix: TokenBitSet,
2715    predicate_dependent: TokenBitSet,
2716}
2717
2718#[derive(Default)]
2719struct SharedAtnCache {
2720    first_set: FirstSetCache,
2721    decision_lookahead: DecisionLookaheadCache,
2722    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2723    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2724    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2725    rule_stop_reach: FxHashMap<usize, bool>,
2726    observable_action_transitions: Option<bool>,
2727    predicate_transitions: Option<bool>,
2728}
2729
2730thread_local! {
2731    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2732        RefCell::new(FxHashMap::default());
2733}
2734
2735/// Compound key for `SHARED_ATN_CACHES`.
2736///
2737/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2738/// pointer identifies one grammar for the program's lifetime. For the
2739/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2740/// the secondary fields below catch the pointer collision: a new grammar
2741/// would need to match all of `(states ptr, states len, max_token_type)` to
2742/// be mistaken for the dropped one. That combination changing under us
2743/// without a rebuild is implausible enough to treat as a bug; bundling them
2744/// into the key is otherwise a few extra bytes per lookup.
2745#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2746struct SharedAtnCacheKey {
2747    atn: usize,
2748    states: usize,
2749    state_count: usize,
2750    max_token_type: i32,
2751}
2752
2753impl SharedAtnCacheKey {
2754    fn for_atn(atn: &Atn) -> Self {
2755        let (states, state_count) = atn.storage_identity();
2756        Self {
2757            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2758            states,
2759            state_count,
2760            max_token_type: atn.max_token_type(),
2761        }
2762    }
2763}
2764
2765fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2766    SHARED_ATN_CACHES.with(|cell| {
2767        let key = SharedAtnCacheKey::for_atn(atn);
2768        let mut map = cell.borrow_mut();
2769        let cache = map.entry(key).or_default();
2770        f(&mut cache.first_set)
2771    })
2772}
2773
2774fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
2775    SHARED_ATN_CACHES.with(|cell| {
2776        let key = SharedAtnCacheKey::for_atn(atn);
2777        let mut map = cell.borrow_mut();
2778        let cache = map.entry(key).or_default();
2779        f(cache)
2780    })
2781}
2782
2783/// Per-decision-state cached look-1 sets for each outgoing transition.
2784///
2785/// At a multi-alternative state, the recognizer would otherwise speculatively
2786/// walk every alternative even when only one can possibly accept the current
2787/// lookahead. Caching the look-1 set per transition lets us prune the
2788/// non-viable transitions before recursing — the same SLL prediction trick
2789/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
2790/// filter rather than a full DFA.
2791#[derive(Debug, Default)]
2792struct DecisionLookahead {
2793    transitions: Vec<TransitionLookSet>,
2794}
2795
2796/// Look-1 information for one outgoing transition.
2797///
2798/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
2799/// can reach the rule stop without consuming a token (e.g. an empty alt).
2800/// Nullable transitions cannot be pruned: they may still be the right path
2801/// when the lookahead consumes nothing further inside the current rule.
2802#[derive(Clone, Debug, Default)]
2803struct TransitionLookSet {
2804    symbols: TokenBitSet,
2805    nullable: bool,
2806}
2807
2808/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
2809/// rarely-touched fields keeps the recursive helpers below the function-arity
2810/// lint and lets every nested call thread the same cache and cycle guards.
2811struct FirstSetCtx<'a> {
2812    cache: &'a mut FirstSetCache,
2813    in_progress: BTreeSet<(usize, usize)>,
2814    hit_cycle: bool,
2815}
2816
2817/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
2818/// shared cache and tolerating recursive nullable rule chains. Mutually
2819/// recursive rules cannot stack-overflow because callers in flight are tracked
2820/// in `ctx.in_progress`; revisits return without recursing, and the partial
2821/// result is cached only when no cycle was detected during its computation.
2822///
2823/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
2824/// rule-call probes only pay a reference bump.
2825fn rule_first_set(
2826    atn: &Atn,
2827    target: usize,
2828    rule_stop_state: usize,
2829    cache: &mut FirstSetCache,
2830) -> Rc<FirstSet> {
2831    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
2832        return Rc::clone(cached);
2833    }
2834    let mut ctx = FirstSetCtx {
2835        cache,
2836        in_progress: BTreeSet::new(),
2837        hit_cycle: false,
2838    };
2839    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
2840}
2841
2842fn rule_first_set_cached(
2843    atn: &Atn,
2844    target: usize,
2845    rule_stop_state: usize,
2846    ctx: &mut FirstSetCtx<'_>,
2847) -> Rc<FirstSet> {
2848    let key = (target, rule_stop_state);
2849    if let Some(cached) = ctx.cache.get(&key) {
2850        return Rc::clone(cached);
2851    }
2852    if !ctx.in_progress.insert(key) {
2853        // Cycle: a caller above is already computing this entry. Return an
2854        // empty FIRST set; that caller's traversal supplies the contributions
2855        // from the rule's other alternatives.
2856        return Rc::new(FirstSet::default());
2857    }
2858    let saved_hit_cycle = ctx.hit_cycle;
2859    ctx.hit_cycle = false;
2860    let mut first = FirstSet::default();
2861    let mut visited = BTreeSet::new();
2862    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
2863    ctx.in_progress.remove(&key);
2864    let entry = Rc::new(first);
2865    if !ctx.hit_cycle {
2866        ctx.cache.insert(key, Rc::clone(&entry));
2867    }
2868    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2869    entry
2870}
2871
2872/// Returns the look-1 set for traversing `transition` while still inside the
2873/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
2874/// prunes transitions whose look-1 cannot accept the current lookahead.
2875fn transition_first_set(
2876    atn: &Atn,
2877    transition: ParserTransition<'_>,
2878    rule_stop_state: usize,
2879    cache: &mut FirstSetCache,
2880) -> TransitionLookSet {
2881    match &transition.data() {
2882        Transition::Atom { label, .. } => {
2883            let mut symbols = TokenBitSet::default();
2884            symbols.insert(*label);
2885            TransitionLookSet {
2886                symbols,
2887                nullable: false,
2888            }
2889        }
2890        Transition::Range { start, stop, .. } => {
2891            let mut symbols = TokenBitSet::default();
2892            symbols.extend_range(*start, *stop);
2893            TransitionLookSet {
2894                symbols,
2895                nullable: false,
2896            }
2897        }
2898        Transition::Set { set, .. } => {
2899            let mut symbols = TokenBitSet::default();
2900            for (start, stop) in set.ranges() {
2901                symbols.extend_range(start, stop);
2902            }
2903            TransitionLookSet {
2904                symbols,
2905                nullable: false,
2906            }
2907        }
2908        Transition::NotSet { set, .. } => {
2909            let max = atn.max_token_type();
2910            let mut symbols = TokenBitSet::default();
2911            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
2912            TransitionLookSet {
2913                symbols,
2914                nullable: false,
2915            }
2916        }
2917        Transition::Wildcard { .. } => {
2918            let mut symbols = TokenBitSet::default();
2919            symbols.extend_range(1, atn.max_token_type());
2920            TransitionLookSet {
2921                symbols,
2922                nullable: false,
2923            }
2924        }
2925        Transition::Epsilon { target }
2926        | Transition::Action { target, .. }
2927        | Transition::Predicate { target, .. }
2928        | Transition::Precedence { target, .. } => {
2929            // Walk the closure starting at `target` until a consuming transition
2930            // is reached or the rule stop state is hit.
2931            let first = rule_first_set(atn, *target, rule_stop_state, cache);
2932            TransitionLookSet {
2933                symbols: first.symbols.clone(),
2934                nullable: first.nullable,
2935            }
2936        }
2937        Transition::Rule {
2938            target,
2939            rule_index,
2940            follow_state,
2941            ..
2942        } => {
2943            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
2944                return TransitionLookSet::default();
2945            };
2946            let child = rule_first_set(atn, *target, child_stop, cache);
2947            let mut symbols = child.symbols.clone();
2948            let nullable = if child.nullable {
2949                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
2950                symbols.extend_from(&follow.symbols);
2951                follow.nullable
2952            } else {
2953                false
2954            };
2955            TransitionLookSet { symbols, nullable }
2956        }
2957    }
2958}
2959
2960/// Reports whether `transition` can be pruned at a multi-alt state because
2961/// its cached look-1 cannot accept the current lookahead.
2962///
2963/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
2964/// Rule/Precedence) so consuming transitions still reach the
2965/// `matches`+recovery path that surfaces single-token deletion / insertion
2966/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2967/// transition is pruned, its FIRST set is folded into `expected` so failed
2968/// parses produce the same `mismatched input ... expecting ...` diagnostic
2969/// the no-prefilter baseline would emit.
2970/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2971/// one transition's FIRST set and no transition is nullable. Used as an
2972/// LL(1) commit point: when prediction is unambiguous from the lookahead
2973/// alone, the recursive recognizer can skip every other alt without paying
2974/// for the per-transition filter probe.
2975///
2976/// `None` signals the caller to fall back to per-transition lookahead
2977/// filtering. Returning `Some` for an alt whose transition cannot actually
2978/// match would prune the only viable parse path; this is why we require
2979/// strict disjointness *and* no nullable transitions in the decision.
2980fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2981    let mut chosen: Option<usize> = None;
2982    for (index, transition) in entry.transitions.iter().enumerate() {
2983        if transition.nullable {
2984            return None;
2985        }
2986        if transition.symbols.contains(symbol) {
2987            if chosen.is_some() {
2988                return None;
2989            }
2990            chosen = Some(index);
2991        }
2992    }
2993    chosen
2994}
2995
2996/// Returns the unique greedy alt index (0-based) selected by the current
2997/// lookahead.
2998///
2999/// The shortcut is intentionally conservative around nullable exits. If the
3000/// current symbol can start a consuming alternative and an empty alternative is
3001/// also present, one-token lookahead is not enough to know whether the symbol
3002/// belongs to the current construct or to its caller's follow set. `None`
3003/// signals the caller to fall back to adaptive prediction.
3004fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
3005    let mut matching_non_nullable_alt = None;
3006    let mut nullable_alt = None;
3007    for (index, transition) in entry.transitions.iter().enumerate() {
3008        if transition.nullable {
3009            if nullable_alt.is_some() {
3010                return None;
3011            }
3012            nullable_alt = Some(index);
3013        }
3014        if transition.symbols.contains(symbol) {
3015            if transition.nullable {
3016                continue;
3017            }
3018            if matching_non_nullable_alt.is_some() {
3019                return None;
3020            }
3021            matching_non_nullable_alt = Some(index);
3022        }
3023    }
3024    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
3025        return None;
3026    }
3027    if non_greedy {
3028        nullable_alt.or(matching_non_nullable_alt)
3029    } else {
3030        matching_non_nullable_alt.or(nullable_alt)
3031    }
3032}
3033
3034fn should_skip_via_lookahead(
3035    transition_kind: ParserTransitionKind,
3036    transition_index: usize,
3037    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
3038    index: usize,
3039    record_expected: bool,
3040    expected: &mut ExpectedTokens,
3041) -> bool {
3042    let prune_non_consuming = matches!(
3043        transition_kind,
3044        ParserTransitionKind::Epsilon
3045            | ParserTransitionKind::Action
3046            | ParserTransitionKind::Predicate
3047            | ParserTransitionKind::Rule
3048            | ParserTransitionKind::Precedence
3049    );
3050    if !prune_non_consuming {
3051        return false;
3052    }
3053    let Some((symbol, entry)) = lookahead_filter else {
3054        return false;
3055    };
3056    let Some(set) = entry.transitions.get(transition_index) else {
3057        return false;
3058    };
3059    if set.symbols.contains(*symbol) || set.nullable {
3060        return false;
3061    }
3062    if record_expected && !set.symbols.is_empty() {
3063        record_pruned_transition_expected(set, index, expected);
3064    }
3065    true
3066}
3067
3068fn should_skip_rule_via_first_set(
3069    first: &FirstSet,
3070    symbol: i32,
3071    record_expected: bool,
3072    index: usize,
3073    expected: &mut ExpectedTokens,
3074) -> bool {
3075    if first.nullable || first.symbols.contains(symbol) {
3076        return false;
3077    }
3078    if record_expected && !first.symbols.is_empty() {
3079        record_token_bit_expected(&first.symbols, index, expected);
3080    }
3081    true
3082}
3083
3084fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
3085    match expected.index {
3086        Some(current) if index < current => {}
3087        Some(current) if index == current => {
3088            symbols.extend_btree_set(&mut expected.symbols);
3089        }
3090        _ => {
3091            expected.index = Some(index);
3092            expected.symbols = symbols.to_btree_set();
3093        }
3094    }
3095}
3096
3097/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
3098fn record_pruned_transition_expected(
3099    set: &TransitionLookSet,
3100    index: usize,
3101    expected: &mut ExpectedTokens,
3102) {
3103    match expected.index {
3104        Some(current) if index < current => {}
3105        Some(current) if index == current => {
3106            set.symbols.extend_btree_set(&mut expected.symbols);
3107        }
3108        _ => {
3109            expected.index = Some(index);
3110            expected.symbols = set.symbols.to_btree_set();
3111        }
3112    }
3113}
3114
3115fn rule_first_set_inner(
3116    atn: &Atn,
3117    state_number: usize,
3118    rule_stop_state: usize,
3119    ctx: &mut FirstSetCtx<'_>,
3120    visited: &mut BTreeSet<usize>,
3121    first: &mut FirstSet,
3122) {
3123    if !visited.insert(state_number) {
3124        return;
3125    }
3126    if state_number == rule_stop_state {
3127        first.nullable = true;
3128        return;
3129    }
3130    let Some(state) = atn.state(state_number) else {
3131        return;
3132    };
3133    for transition in &state.transitions() {
3134        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3135        if !transition_symbols.is_empty() {
3136            first.symbols.extend_iter(transition_symbols);
3137            continue;
3138        }
3139        match &transition.data() {
3140            Transition::Epsilon { target }
3141            | Transition::Action { target, .. }
3142            | Transition::Predicate { target, .. }
3143            | Transition::Precedence { target, .. } => {
3144                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
3145            }
3146            Transition::Rule {
3147                target,
3148                rule_index,
3149                follow_state,
3150                ..
3151            } => {
3152                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3153                    continue;
3154                };
3155                let child_key = (*target, child_stop);
3156                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
3157                    ctx.hit_cycle = true;
3158                }
3159                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
3160                first.symbols.extend_from(&child.symbols);
3161                if child.nullable {
3162                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
3163                }
3164            }
3165            Transition::Atom { .. }
3166            | Transition::Range { .. }
3167            | Transition::Set { .. }
3168            | Transition::NotSet { .. }
3169            | Transition::Wildcard { .. } => {}
3170        }
3171    }
3172}
3173
3174/// Returns token types that can resume parsing from `state_number` after a
3175/// failed child rule, following rule calls as well as epsilon transitions.
3176fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
3177    let mut symbols = BTreeSet::new();
3178    state_sync_symbols_inner(
3179        atn,
3180        state_number,
3181        stop_state,
3182        &mut BTreeSet::new(),
3183        &mut symbols,
3184    );
3185    symbols
3186}
3187
3188/// Walks epsilon-like continuations from a parent follow state until it finds
3189/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
3190fn state_sync_symbols_inner(
3191    atn: &Atn,
3192    state_number: usize,
3193    stop_state: usize,
3194    visited: &mut BTreeSet<usize>,
3195    symbols: &mut BTreeSet<i32>,
3196) {
3197    if !visited.insert(state_number) {
3198        return;
3199    }
3200    if state_number == stop_state {
3201        symbols.insert(TOKEN_EOF);
3202        return;
3203    }
3204    let Some(state) = atn.state(state_number) else {
3205        return;
3206    };
3207    for transition in &state.transitions() {
3208        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3209        if transition_symbols.is_empty() {
3210            match &transition.data() {
3211                Transition::Rule { target, .. }
3212                | Transition::Epsilon { target }
3213                | Transition::Action { target, .. }
3214                | Transition::Predicate { target, .. }
3215                | Transition::Precedence { target, .. } => {
3216                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
3217                }
3218                Transition::Atom { .. }
3219                | Transition::Range { .. }
3220                | Transition::Set { .. }
3221                | Transition::NotSet { .. }
3222                | Transition::Wildcard { .. } => {}
3223            }
3224        } else {
3225            symbols.extend(transition_symbols);
3226        }
3227    }
3228}
3229
3230#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3231struct OperatorSymbolReachability {
3232    /// One token completes an unconditional operator token-prefix.
3233    single_token: bool,
3234    /// An unconditional operator path requires more tokens before its operand.
3235    multi_token: bool,
3236    /// At least one matching operator path depends on a semantic predicate.
3237    predicate_dependent: bool,
3238}
3239
3240impl OperatorSymbolReachability {
3241    const ADAPTIVE_FALLBACK: Self = Self {
3242        single_token: false,
3243        multi_token: false,
3244        predicate_dependent: true,
3245    };
3246
3247    const fn single_token(predicate_dependent: bool) -> Self {
3248        if predicate_dependent {
3249            Self {
3250                single_token: false,
3251                multi_token: false,
3252                predicate_dependent: true,
3253            }
3254        } else {
3255            Self {
3256                single_token: true,
3257                multi_token: false,
3258                predicate_dependent: false,
3259            }
3260        }
3261    }
3262
3263    const fn multi_token(predicate_dependent: bool) -> Self {
3264        if predicate_dependent {
3265            Self {
3266                single_token: false,
3267                multi_token: false,
3268                predicate_dependent: true,
3269            }
3270        } else {
3271            Self {
3272                single_token: false,
3273                multi_token: true,
3274                predicate_dependent: false,
3275            }
3276        }
3277    }
3278
3279    const fn union(self, other: Self) -> Self {
3280        Self {
3281            single_token: self.single_token || other.single_token,
3282            multi_token: self.multi_token || other.multi_token,
3283            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3284        }
3285    }
3286}
3287
3288#[derive(Clone, Copy)]
3289struct OperatorReachabilityRequest {
3290    symbol: i32,
3291    precedence: i32,
3292    predicate_dependent: bool,
3293    operator_rule_index: usize,
3294}
3295
3296#[derive(Clone, Copy, Debug)]
3297struct OperatorRuleContinuation {
3298    stop_state: usize,
3299    follow_state: usize,
3300    return_precedence: i32,
3301}
3302
3303struct NullablePrecedenceCtx {
3304    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3305    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3306    hit_cycle: bool,
3307}
3308
3309fn state_is_nullable_with_precedence(
3310    atn: &Atn,
3311    state_number: usize,
3312    stop_state_number: usize,
3313    precedence: i32,
3314    allow_predicates: bool,
3315    ctx: &mut NullablePrecedenceCtx,
3316) -> bool {
3317    let saved_hit_cycle = ctx.hit_cycle;
3318    ctx.hit_cycle = false;
3319    let nullable = state_is_nullable_with_precedence_cached(
3320        atn,
3321        state_number,
3322        stop_state_number,
3323        precedence,
3324        allow_predicates,
3325        ctx,
3326    );
3327    ctx.hit_cycle = saved_hit_cycle;
3328    nullable
3329}
3330
3331fn state_is_nullable_with_precedence_cached(
3332    atn: &Atn,
3333    state_number: usize,
3334    stop_state_number: usize,
3335    precedence: i32,
3336    allow_predicates: bool,
3337    ctx: &mut NullablePrecedenceCtx,
3338) -> bool {
3339    if state_number == stop_state_number {
3340        return true;
3341    }
3342    let key = (
3343        state_number,
3344        stop_state_number,
3345        precedence,
3346        allow_predicates,
3347    );
3348    if let Some(cached) = ctx.cache.get(&key) {
3349        return *cached;
3350    }
3351    if !ctx.in_progress.insert(key) {
3352        ctx.hit_cycle = true;
3353        return false;
3354    }
3355    let saved_hit_cycle = ctx.hit_cycle;
3356    ctx.hit_cycle = false;
3357    let nullable = atn.state(state_number).is_some_and(|state| {
3358        state
3359            .transitions()
3360            .iter()
3361            .any(|transition| match &transition.data() {
3362                Transition::Rule {
3363                    target,
3364                    rule_index,
3365                    follow_state,
3366                    precedence: rule_precedence,
3367                } => {
3368                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3369                        return false;
3370                    };
3371                    state_is_nullable_with_precedence_cached(
3372                        atn,
3373                        *target,
3374                        child_stop,
3375                        *rule_precedence,
3376                        allow_predicates,
3377                        ctx,
3378                    ) && state_is_nullable_with_precedence_cached(
3379                        atn,
3380                        *follow_state,
3381                        stop_state_number,
3382                        precedence,
3383                        allow_predicates,
3384                        ctx,
3385                    )
3386                }
3387                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3388                    state_is_nullable_with_precedence_cached(
3389                        atn,
3390                        *target,
3391                        stop_state_number,
3392                        precedence,
3393                        allow_predicates,
3394                        ctx,
3395                    )
3396                }
3397                Transition::Predicate { target, .. } if allow_predicates => {
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::Precedence {
3408                    target,
3409                    precedence: transition_precedence,
3410                } if *transition_precedence >= precedence => {
3411                    state_is_nullable_with_precedence_cached(
3412                        atn,
3413                        *target,
3414                        stop_state_number,
3415                        precedence,
3416                        allow_predicates,
3417                        ctx,
3418                    )
3419                }
3420                Transition::Atom { .. }
3421                | Transition::Range { .. }
3422                | Transition::Set { .. }
3423                | Transition::NotSet { .. }
3424                | Transition::Wildcard { .. }
3425                | Transition::Predicate { .. }
3426                | Transition::Precedence { .. } => false,
3427            })
3428    });
3429    ctx.in_progress.remove(&key);
3430    if !ctx.hit_cycle {
3431        ctx.cache.insert(key, nullable);
3432    }
3433    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3434    nullable
3435}
3436
3437/// Classifies what remains after the operator's first token is matched.
3438fn state_operator_token_prefix_reachability(
3439    atn: &Atn,
3440    state_number: usize,
3441    request: OperatorReachabilityRequest,
3442    continuations: &[OperatorRuleContinuation],
3443    visited: &mut BTreeSet<(usize, i32, bool)>,
3444) -> OperatorSymbolReachability {
3445    let key = (
3446        state_number,
3447        request.precedence,
3448        request.predicate_dependent,
3449    );
3450    if !visited.insert(key) {
3451        // Recursive helper rules can grow the return stack without consuming
3452        // input. Delegate cycles to adaptive prediction instead of forcing a
3453        // potentially incomplete one-token answer.
3454        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3455    }
3456    if let Some((continuation, remaining)) = continuations.split_last()
3457        && state_number == continuation.stop_state
3458    {
3459        let result = state_operator_token_prefix_reachability(
3460            atn,
3461            continuation.follow_state,
3462            OperatorReachabilityRequest {
3463                precedence: continuation.return_precedence,
3464                ..request
3465            },
3466            remaining,
3467            visited,
3468        );
3469        visited.remove(&key);
3470        return result;
3471    }
3472    let Some(state) = atn.state(state_number) else {
3473        visited.remove(&key);
3474        return OperatorSymbolReachability::default();
3475    };
3476    let completes_operator = match state.kind() {
3477        AtnStateKind::RuleStop => continuations.is_empty(),
3478        AtnStateKind::StarLoopBack
3479        | AtnStateKind::StarLoopEntry
3480        | AtnStateKind::PlusLoopBack
3481        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3482        _ => false,
3483    };
3484    if completes_operator {
3485        visited.remove(&key);
3486        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3487    }
3488    let mut reachability = OperatorSymbolReachability::default();
3489    for transition in &state.transitions() {
3490        let transition_reachability = match &transition.data() {
3491            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3492                OperatorSymbolReachability::single_token(request.predicate_dependent)
3493            }
3494            Transition::Rule {
3495                target,
3496                rule_index,
3497                follow_state,
3498                precedence: rule_precedence,
3499            } => {
3500                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3501                    continue;
3502                };
3503                let mut nested = continuations.to_vec();
3504                nested.push(OperatorRuleContinuation {
3505                    stop_state: child_stop,
3506                    follow_state: *follow_state,
3507                    return_precedence: request.precedence,
3508                });
3509                state_operator_token_prefix_reachability(
3510                    atn,
3511                    *target,
3512                    OperatorReachabilityRequest {
3513                        precedence: *rule_precedence,
3514                        ..request
3515                    },
3516                    &nested,
3517                    visited,
3518                )
3519            }
3520            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3521                state_operator_token_prefix_reachability(
3522                    atn,
3523                    *target,
3524                    request,
3525                    continuations,
3526                    visited,
3527                )
3528            }
3529            Transition::Precedence {
3530                target,
3531                precedence: transition_precedence,
3532            } => {
3533                if *transition_precedence < request.precedence {
3534                    OperatorSymbolReachability::default()
3535                } else {
3536                    state_operator_token_prefix_reachability(
3537                        atn,
3538                        *target,
3539                        request,
3540                        continuations,
3541                        visited,
3542                    )
3543                }
3544            }
3545            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3546                atn,
3547                *target,
3548                OperatorReachabilityRequest {
3549                    predicate_dependent: true,
3550                    ..request
3551                },
3552                continuations,
3553                visited,
3554            ),
3555            Transition::Atom { .. }
3556            | Transition::Range { .. }
3557            | Transition::Set { .. }
3558            | Transition::NotSet { .. }
3559            | Transition::Wildcard { .. } => {
3560                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3561            }
3562        };
3563        reachability = reachability.union(transition_reachability);
3564    }
3565    visited.remove(&key);
3566    reachability
3567}
3568
3569fn state_can_reach_symbol_with_precedence(
3570    atn: &Atn,
3571    state_number: usize,
3572    request: OperatorReachabilityRequest,
3573    nullable_ctx: &mut NullablePrecedenceCtx,
3574    continuations: &mut Vec<OperatorRuleContinuation>,
3575    visited: &mut BTreeSet<(usize, i32, bool)>,
3576) -> OperatorSymbolReachability {
3577    let key = (
3578        state_number,
3579        request.precedence,
3580        request.predicate_dependent,
3581    );
3582    if !visited.insert(key) {
3583        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3584    }
3585    let Some(state) = atn.state(state_number) else {
3586        visited.remove(&key);
3587        return OperatorSymbolReachability::default();
3588    };
3589    let mut reachability = OperatorSymbolReachability::default();
3590    for transition in &state.transitions() {
3591        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3592            reachability = reachability.union(state_operator_token_prefix_reachability(
3593                atn,
3594                transition.target(),
3595                request,
3596                continuations,
3597                &mut BTreeSet::new(),
3598            ));
3599            continue;
3600        }
3601        let transition_reachability = match &transition.data() {
3602            Transition::Rule {
3603                target,
3604                rule_index,
3605                follow_state,
3606                precedence: rule_precedence,
3607            } => {
3608                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3609                    continue;
3610                };
3611                continuations.push(OperatorRuleContinuation {
3612                    stop_state: child_stop,
3613                    follow_state: *follow_state,
3614                    return_precedence: request.precedence,
3615                });
3616                let mut result = state_can_reach_symbol_with_precedence(
3617                    atn,
3618                    *target,
3619                    OperatorReachabilityRequest {
3620                        precedence: *rule_precedence,
3621                        ..request
3622                    },
3623                    nullable_ctx,
3624                    continuations,
3625                    visited,
3626                );
3627                continuations.pop();
3628                if state_is_nullable_with_precedence(
3629                    atn,
3630                    *target,
3631                    child_stop,
3632                    *rule_precedence,
3633                    true,
3634                    nullable_ctx,
3635                ) {
3636                    let child_predicate_dependent = request.predicate_dependent
3637                        || !state_is_nullable_with_precedence(
3638                            atn,
3639                            *target,
3640                            child_stop,
3641                            *rule_precedence,
3642                            false,
3643                            nullable_ctx,
3644                        );
3645                    result = result.union(state_can_reach_symbol_with_precedence(
3646                        atn,
3647                        *follow_state,
3648                        OperatorReachabilityRequest {
3649                            predicate_dependent: child_predicate_dependent,
3650                            ..request
3651                        },
3652                        nullable_ctx,
3653                        continuations,
3654                        visited,
3655                    ));
3656                }
3657                result
3658            }
3659            Transition::Epsilon { target }
3660            | Transition::Action { target, .. }
3661            | Transition::Precedence { target, .. } => {
3662                if matches!(
3663                    &transition.data(),
3664                    Transition::Precedence {
3665                        precedence: transition_precedence,
3666                        ..
3667                    } if *transition_precedence < request.precedence
3668                ) {
3669                    continue;
3670                }
3671                state_can_reach_symbol_with_precedence(
3672                    atn,
3673                    *target,
3674                    request,
3675                    nullable_ctx,
3676                    continuations,
3677                    visited,
3678                )
3679            }
3680            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3681                atn,
3682                *target,
3683                OperatorReachabilityRequest {
3684                    predicate_dependent: true,
3685                    ..request
3686                },
3687                nullable_ctx,
3688                continuations,
3689                visited,
3690            ),
3691            Transition::Atom { .. }
3692            | Transition::Range { .. }
3693            | Transition::Set { .. }
3694            | Transition::NotSet { .. }
3695            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3696        };
3697        reachability = reachability.union(transition_reachability);
3698    }
3699    visited.remove(&key);
3700    reachability
3701}
3702
3703fn left_recursive_operator_lookahead(
3704    atn: &Atn,
3705    state_number: usize,
3706    precedence: i32,
3707) -> LeftRecursiveOperatorLookahead {
3708    let Some(state) = atn.state(state_number) else {
3709        return LeftRecursiveOperatorLookahead::default();
3710    };
3711    let Some(operator_rule_index) = state.rule_index() else {
3712        return LeftRecursiveOperatorLookahead::default();
3713    };
3714    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3715    let mut nullable_ctx = NullablePrecedenceCtx {
3716        cache: FxHashMap::default(),
3717        in_progress: BTreeSet::new(),
3718        hit_cycle: false,
3719    };
3720    for transition in &state.transitions() {
3721        let target = transition.target();
3722        if atn
3723            .state(target)
3724            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3725        {
3726            continue;
3727        }
3728        for symbol in 1..=atn.max_token_type() {
3729            let reachability = state_can_reach_symbol_with_precedence(
3730                atn,
3731                target,
3732                OperatorReachabilityRequest {
3733                    symbol,
3734                    precedence,
3735                    predicate_dependent: false,
3736                    operator_rule_index,
3737                },
3738                &mut nullable_ctx,
3739                &mut Vec::new(),
3740                &mut BTreeSet::new(),
3741            );
3742            if reachability.single_token {
3743                lookahead.single_token.insert(symbol);
3744            }
3745            if reachability.multi_token {
3746                lookahead.multi_token_prefix.insert(symbol);
3747            }
3748            if reachability.predicate_dependent {
3749                lookahead.predicate_dependent.insert(symbol);
3750            }
3751        }
3752    }
3753    lookahead
3754}
3755
3756#[derive(Debug, Default)]
3757struct StateBeforeStopLookahead {
3758    symbols: TokenBitSet,
3759    reaches_context_boundary: bool,
3760}
3761
3762fn state_before_stop_lookahead(
3763    atn: &Atn,
3764    state_number: usize,
3765    stop_state_number: usize,
3766) -> Rc<StateBeforeStopLookahead> {
3767    with_shared_atn_caches(atn, |cache| {
3768        let key = (state_number, stop_state_number);
3769        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
3770            return Rc::clone(cached);
3771        }
3772        let mut lookahead = StateBeforeStopLookahead::default();
3773        state_before_stop_lookahead_inner(
3774            atn,
3775            state_number,
3776            stop_state_number,
3777            &mut BTreeSet::new(),
3778            &mut cache.first_set,
3779            &mut lookahead,
3780        );
3781        let lookahead = Rc::new(lookahead);
3782        cache
3783            .state_before_stop_lookahead
3784            .insert(key, Rc::clone(&lookahead));
3785        lookahead
3786    })
3787}
3788
3789fn state_before_stop_lookahead_inner(
3790    atn: &Atn,
3791    state_number: usize,
3792    stop_state_number: usize,
3793    visited: &mut BTreeSet<usize>,
3794    first_set_cache: &mut FirstSetCache,
3795    lookahead: &mut StateBeforeStopLookahead,
3796) {
3797    if state_number == stop_state_number {
3798        lookahead.reaches_context_boundary = true;
3799        return;
3800    }
3801    if !visited.insert(state_number) {
3802        return;
3803    }
3804    let Some(state) = atn.state(state_number) else {
3805        return;
3806    };
3807    if state.kind() == AtnStateKind::RuleStop {
3808        lookahead.reaches_context_boundary = true;
3809        return;
3810    }
3811    for transition in &state.transitions() {
3812        match &transition.data() {
3813            Transition::Epsilon { target }
3814            | Transition::Action { target, .. }
3815            | Transition::Predicate { target, .. }
3816            | Transition::Precedence { target, .. } => {
3817                state_before_stop_lookahead_inner(
3818                    atn,
3819                    *target,
3820                    stop_state_number,
3821                    visited,
3822                    first_set_cache,
3823                    lookahead,
3824                );
3825            }
3826            Transition::Rule {
3827                target,
3828                rule_index,
3829                follow_state,
3830                ..
3831            } => {
3832                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3833                    continue;
3834                };
3835                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
3836                lookahead.symbols.extend_from(&child.symbols);
3837                if child.nullable {
3838                    state_before_stop_lookahead_inner(
3839                        atn,
3840                        *follow_state,
3841                        stop_state_number,
3842                        visited,
3843                        first_set_cache,
3844                        lookahead,
3845                    );
3846                }
3847            }
3848            Transition::Atom { .. }
3849            | Transition::Range { .. }
3850            | Transition::Set { .. }
3851            | Transition::NotSet { .. }
3852            | Transition::Wildcard { .. } => {
3853                lookahead.symbols.extend_iter(transition_expected_symbols(
3854                    transition,
3855                    atn.max_token_type(),
3856                ));
3857            }
3858        }
3859    }
3860}
3861
3862fn caller_context_can_match_symbol_before_state(
3863    atn: &Atn,
3864    return_states: impl DoubleEndedIterator<Item = usize>,
3865    stop_state_number: usize,
3866    symbol: i32,
3867) -> bool {
3868    for return_state in return_states.rev() {
3869        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
3870        if lookahead.symbols.contains(symbol) {
3871            return true;
3872        }
3873        if !lookahead.reaches_context_boundary {
3874            return false;
3875        }
3876    }
3877    false
3878}
3879
3880/// Carries recovery expectations and their restart state through epsilon-only
3881/// paths. ANTLR can report and repair at the decision state even when the
3882/// failed consuming transition is nested under block or loop epsilon edges.
3883fn next_recovery_context(
3884    atn: &Atn,
3885    state: AtnState<'_>,
3886    inherited: &BTreeSet<i32>,
3887    inherited_state: Option<usize>,
3888) -> (BTreeSet<i32>, Option<usize>) {
3889    let state_symbols = state_expected_symbols(atn, state.state_number());
3890    if state.transitions().len() > 1 && !state_symbols.is_empty() {
3891        let mut symbols = state_symbols;
3892        symbols.extend(inherited.iter().copied());
3893        return (symbols, Some(state.state_number()));
3894    }
3895    (inherited.clone(), inherited_state)
3896}
3897
3898fn recovery_expected_symbols(
3899    atn: &Atn,
3900    state_number: usize,
3901    inherited: &BTreeSet<i32>,
3902) -> BTreeSet<i32> {
3903    let mut symbols = state_expected_symbols(atn, state_number);
3904    symbols.extend(inherited.iter().copied());
3905    symbols
3906}
3907
3908/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
3909/// parser's cached state-expected-symbols sets and the inherited `Rc`
3910/// without copying when the state cannot widen recovery.
3911fn fast_next_recovery_context<S, H>(
3912    parser: &mut BaseParser<S, H>,
3913    atn: &Atn,
3914    state: AtnState<'_>,
3915    inherited: &Rc<BTreeSet<i32>>,
3916    inherited_state: Option<usize>,
3917) -> (Rc<BTreeSet<i32>>, Option<usize>)
3918where
3919    S: TokenSource,
3920    H: SemanticHooks,
3921{
3922    if state.transitions().len() <= 1 {
3923        return (Rc::clone(inherited), inherited_state);
3924    }
3925    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
3926    if state_symbols.is_empty() {
3927        return (Rc::clone(inherited), inherited_state);
3928    }
3929    if inherited.is_empty() {
3930        return (state_symbols, Some(state.state_number()));
3931    }
3932    if Rc::ptr_eq(&state_symbols, inherited) {
3933        return (state_symbols, Some(state.state_number()));
3934    }
3935    let mut combined = (*state_symbols).clone();
3936    combined.extend(inherited.iter().copied());
3937    (
3938        parser.intern_recovery_symbols(combined),
3939        Some(state.state_number()),
3940    )
3941}
3942
3943/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
3944/// cached state-expected-symbols and avoids cloning when no widening is
3945/// needed.
3946fn fast_recovery_expected_symbols<S, H>(
3947    parser: &mut BaseParser<S, H>,
3948    atn: &Atn,
3949    state_number: usize,
3950    inherited: &Rc<BTreeSet<i32>>,
3951) -> Rc<BTreeSet<i32>>
3952where
3953    S: TokenSource,
3954    H: SemanticHooks,
3955{
3956    let cached = parser.cached_state_expected_symbols(atn, state_number);
3957    if inherited.is_empty() {
3958        return cached;
3959    }
3960    if cached.is_empty() {
3961        return Rc::clone(inherited);
3962    }
3963    if Rc::ptr_eq(&cached, inherited) {
3964        return cached;
3965    }
3966    let mut combined = (*cached).clone();
3967    combined.extend(inherited.iter().copied());
3968    parser.intern_recovery_symbols(combined)
3969}
3970
3971struct ParserTableSemCtx<'a> {
3972    member_values: &'a mut MemberEnv,
3973    return_values: &'a mut BTreeMap<String, i64>,
3974}
3975
3976impl semir::PredContext for ParserTableSemCtx<'_> {
3977    type TokenText<'a>
3978        = &'a str
3979    where
3980        Self: 'a;
3981
3982    fn la(&mut self, _offset: isize) -> i64 {
3983        i64::from(TOKEN_EOF)
3984    }
3985
3986    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
3987        None
3988    }
3989
3990    fn token_index_adjacent(&mut self) -> bool {
3991        false
3992    }
3993
3994    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
3995        None
3996    }
3997
3998    fn member(&self, member: usize) -> Option<i64> {
3999        Some(self.member_values.scalar(member).unwrap_or_default())
4000    }
4001
4002    fn member_top(&self, member: usize) -> Option<i64> {
4003        self.member_values.stack_top(member)
4004    }
4005
4006    fn member_len(&self, member: usize) -> usize {
4007        self.member_values.stack_len(member)
4008    }
4009
4010    fn local_arg(&self) -> Option<i64> {
4011        None
4012    }
4013
4014    fn column(&self) -> Option<i64> {
4015        None
4016    }
4017
4018    fn token_start_column(&self) -> Option<i64> {
4019        None
4020    }
4021
4022    fn token_text_so_far(&self) -> Option<String> {
4023        None
4024    }
4025
4026    fn hook(&mut self, _hook: HookId) -> bool {
4027        false
4028    }
4029}
4030
4031impl semir::ActContext for ParserTableSemCtx<'_> {
4032    fn set_member(&mut self, member: usize, value: i64) {
4033        self.member_values.set_scalar(member, value);
4034    }
4035
4036    fn push_member(&mut self, member: usize, value: i64) {
4037        self.member_values.push_stack(member, value);
4038    }
4039
4040    fn pop_member(&mut self, member: usize) -> Option<i64> {
4041        self.member_values.pop_stack(member)
4042    }
4043
4044    fn set_return(&mut self, name: &str, value: i64) {
4045        self.return_values.insert(name.to_owned(), value);
4046    }
4047
4048    fn action_hook(&mut self, _hook: HookId) {}
4049}
4050
4051/// Applies generated integer-member side effects to one speculative path.
4052fn apply_member_actions(
4053    source_state: usize,
4054    actions: &[ParserMemberAction],
4055    semantics: Option<&ParserSemantics>,
4056    values: &mut MemberEnv,
4057) {
4058    for action in actions
4059        .iter()
4060        .filter(|action| action.source_state == source_state)
4061    {
4062        values.add_scalar(action.member, action.delta);
4063    }
4064    let Some(semantics) = semantics else {
4065        return;
4066    };
4067    let mut return_values = BTreeMap::new();
4068    let mut ctx = ParserTableSemCtx {
4069        member_values: values,
4070        return_values: &mut return_values,
4071    };
4072    for action in semantics
4073        .actions
4074        .iter()
4075        .filter(|action| action.source_state == source_state && action.speculative)
4076    {
4077        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
4078    }
4079}
4080
4081/// Returns the speculative member state after replaying one ATN action state.
4082fn member_values_after_action(
4083    source_state: usize,
4084    actions: &[ParserMemberAction],
4085    semantics: Option<&ParserSemantics>,
4086    values: &MemberEnv,
4087) -> MemberEnv {
4088    let mut values = values.clone();
4089    apply_member_actions(source_state, actions, semantics, &mut values);
4090    values
4091}
4092
4093/// Returns the speculative rule-return state after replaying one ATN action.
4094fn return_values_after_action(
4095    source_state: usize,
4096    rule_index: usize,
4097    actions: &[ParserReturnAction],
4098    semantics: Option<&ParserSemantics>,
4099    values: &BTreeMap<String, i64>,
4100) -> BTreeMap<String, i64> {
4101    let mut values = values.clone();
4102    for action in actions
4103        .iter()
4104        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
4105    {
4106        values.insert(action.name.to_owned(), action.value);
4107    }
4108    if let Some(semantics) = semantics {
4109        let mut member_values = MemberEnv::new();
4110        let mut ctx = ParserTableSemCtx {
4111            member_values: &mut member_values,
4112            return_values: &mut values,
4113        };
4114        for action in semantics.actions.iter().filter(|action| {
4115            action.source_state == source_state
4116                && action.rule_index == rule_index
4117                && !action.speculative
4118        }) {
4119            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
4120        }
4121    }
4122    values
4123}
4124
4125/// Resolves the integer argument visible to a child rule invocation.
4126fn rule_local_int_arg(
4127    rule_args: &[ParserRuleArg],
4128    source_state: usize,
4129    rule_index: usize,
4130    local_int_arg: Option<(usize, i64)>,
4131) -> Option<(usize, i64)> {
4132    rule_args
4133        .iter()
4134        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
4135        .map(|arg| {
4136            let value = if arg.inherit_local {
4137                local_int_arg.map_or(arg.value, |(_, value)| value)
4138            } else {
4139                arg.value
4140            };
4141            (rule_index, value)
4142        })
4143}
4144
4145/// Builds the terminal recognition outcome for a path that reached its stop
4146/// state.
4147fn stop_outcome(
4148    index: usize,
4149    consumed_eof: bool,
4150    rule_alt_number: usize,
4151    member_values: MemberEnv,
4152    return_values: BTreeMap<String, i64>,
4153) -> Vec<RecognizeOutcome> {
4154    vec![RecognizeOutcome {
4155        index,
4156        consumed_eof,
4157        alt_number: rule_alt_number,
4158        member_values,
4159        return_values,
4160        diagnostics: DiagnosticSeqId::EMPTY,
4161        decisions: Vec::new(),
4162        actions: Vec::new(),
4163        nodes: NodeSeqId::EMPTY,
4164    }]
4165}
4166
4167fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
4168    with_shared_atn_caches(atn, |cache| {
4169        *cache.observable_action_transitions.get_or_insert_with(|| {
4170            atn.states().any(|state| {
4171                state.transitions().iter().any(|transition| {
4172                    matches!(
4173                        &transition.data(),
4174                        Transition::Action {
4175                            action_index: Some(_),
4176                            ..
4177                        }
4178                    )
4179                })
4180            })
4181        })
4182    })
4183}
4184
4185fn atn_has_predicate_transitions(atn: &Atn) -> bool {
4186    with_shared_atn_caches(atn, |cache| {
4187        *cache.predicate_transitions.get_or_insert_with(|| {
4188            atn.states().any(|state| {
4189                state
4190                    .transitions()
4191                    .iter()
4192                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
4193            })
4194        })
4195    })
4196}
4197
4198/// Reports whether predicates are the only observable semantics the fast
4199/// recognizer must preserve. Without path-local actions, arguments, or return
4200/// state, repeated evaluation at one coordinate and input index receives the
4201/// same runtime context.
4202fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
4203    options.init_action_rules.is_empty()
4204        && !options.track_alt_numbers
4205        && options
4206            .predicates
4207            .iter()
4208            .all(|(_, _, predicate)| predicate.failure_message().is_none())
4209        && options.semantics.is_none_or(|semantics| {
4210            semantics.actions.is_empty()
4211                && semantics
4212                    .predicates
4213                    .iter()
4214                    .all(|predicate| predicate.failure_message.is_none())
4215        })
4216        && options.rule_args.is_empty()
4217        && options.member_actions.is_empty()
4218        && options.return_actions.is_empty()
4219        && !atn_has_observable_action_transitions(atn)
4220}
4221
4222#[derive(Clone, Debug, Eq, PartialEq)]
4223struct RecognizeRequest<'a> {
4224    state_number: usize,
4225    stop_state: usize,
4226    index: usize,
4227    rule_start_index: usize,
4228    decision_start_index: Option<usize>,
4229    init_action_rules: &'a BTreeSet<usize>,
4230    predicates: &'a [(usize, usize, ParserPredicate)],
4231    semantics: Option<&'a ParserSemantics>,
4232    rule_args: &'a [ParserRuleArg],
4233    member_actions: &'a [ParserMemberAction],
4234    return_actions: &'a [ParserReturnAction],
4235    local_int_arg: Option<(usize, i64)>,
4236    member_values: MemberEnv,
4237    return_values: BTreeMap<String, i64>,
4238    rule_alt_number: usize,
4239    track_alt_numbers: bool,
4240    consumed_eof: bool,
4241    committed_decision: bool,
4242    /// Current left-recursive precedence threshold, matching ANTLR's
4243    /// `precpred(_ctx, k)` check for generated precedence rules.
4244    precedence: i32,
4245    depth: usize,
4246    recovery_symbols: BTreeSet<i32>,
4247    recovery_state: Option<usize>,
4248}
4249
4250#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
4251struct RecognizeKey {
4252    state_number: usize,
4253    stop_state: usize,
4254    index: usize,
4255    rule_start_index: usize,
4256    decision_start_index: Option<usize>,
4257    local_int_arg: Option<(usize, i64)>,
4258    member_values: MemberEnv,
4259    return_values: BTreeMap<String, i64>,
4260    rule_alt_number: usize,
4261    track_alt_numbers: bool,
4262    consumed_eof: bool,
4263    committed_decision: bool,
4264    precedence: i32,
4265    recovery_symbols: BTreeSet<i32>,
4266    recovery_state: Option<usize>,
4267}
4268
4269#[derive(Clone, Debug, Eq, PartialEq)]
4270struct EpsilonActionStep {
4271    source_state: usize,
4272    target: usize,
4273    action_rule_index: Option<usize>,
4274    left_recursive_boundary: Option<usize>,
4275    decision: Option<usize>,
4276    decision_start_index: Option<usize>,
4277    alt_number: usize,
4278    recovery_symbols: BTreeSet<i32>,
4279    recovery_state: Option<usize>,
4280}
4281
4282struct RecognizeScratch<'a> {
4283    visiting: &'a mut BTreeSet<RecognizeKey>,
4284    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4285    expected: &'a mut ExpectedTokens,
4286}
4287
4288#[derive(Clone, Debug, Eq, PartialEq)]
4289struct FastRecognizeRequest {
4290    state_number: usize,
4291    stop_state: usize,
4292    index: usize,
4293    rule_start_index: usize,
4294    decision_start_index: Option<usize>,
4295    precedence: i32,
4296    depth: usize,
4297    recovery_symbols: Rc<BTreeSet<i32>>,
4298    recovery_state: Option<usize>,
4299}
4300
4301#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4302struct FastRecognizeTopRequest {
4303    start_state: usize,
4304    stop_state: usize,
4305    start_index: usize,
4306    precedence: i32,
4307    caller_follow_state: Option<usize>,
4308}
4309
4310#[derive(Clone, Copy, Debug)]
4311struct FastPredicateContext<'a> {
4312    predicates: &'a [(usize, usize, ParserPredicate)],
4313    semantics: Option<&'a ParserSemantics>,
4314    member_values: &'a MemberEnv,
4315}
4316
4317#[derive(Clone, Copy, Debug, Default)]
4318struct AltNumberTracking {
4319    public: bool,
4320    context: bool,
4321}
4322
4323impl AltNumberTracking {
4324    const fn any(self) -> bool {
4325        self.public || self.context
4326    }
4327}
4328
4329struct FastRecognizeScratch<'a, 'b> {
4330    predicate_context: Option<FastPredicateContext<'a>>,
4331    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4332    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4333    expected: &'b mut ExpectedTokens,
4334    native_depth: usize,
4335}
4336
4337#[derive(Clone, Copy, Debug)]
4338struct FastRepetitionShape {
4339    enter_target: usize,
4340    exit_target: usize,
4341    body_stop_state: usize,
4342    enter_transition_index: usize,
4343    exit_transition_index: usize,
4344}
4345
4346#[derive(Clone, Copy, Debug)]
4347struct FastRepetitionPath {
4348    index: usize,
4349    deferred_nodes: FastDeferredNodeId,
4350    diagnostics: DiagnosticSeqId,
4351    consumed_eof: bool,
4352}
4353
4354enum FastRepetitionWork {
4355    Enter(FastRepetitionPath),
4356    Exit(FastRepetitionPath),
4357}
4358
4359/// Dense entered/exited coordinate sets for one repetition walk.
4360///
4361/// The start coordinate stays inline so short loops avoid a heap allocation;
4362/// later token indexes use one byte each instead of two hash-table entries.
4363struct FastRepetitionCoordinates {
4364    base_index: usize,
4365    base_state: u8,
4366    later_states: Vec<u8>,
4367}
4368
4369impl FastRepetitionCoordinates {
4370    const ENTERED: u8 = 0;
4371    const EXITED: u8 = 2;
4372
4373    const fn new(base_index: usize) -> Self {
4374        Self {
4375            base_index,
4376            base_state: 0,
4377            later_states: Vec::new(),
4378        }
4379    }
4380
4381    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4382        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4383    }
4384
4385    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4386        self.insert(path.index, path.consumed_eof, Self::EXITED)
4387    }
4388
4389    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4390        let Some(offset) = index.checked_sub(self.base_index) else {
4391            return false;
4392        };
4393        let state = if offset == 0 {
4394            &mut self.base_state
4395        } else {
4396            if self.later_states.len() < offset {
4397                self.later_states.resize(offset, 0);
4398            }
4399            &mut self.later_states[offset - 1]
4400        };
4401        let bit = 1 << (base_bit + u8::from(consumed_eof));
4402        let is_new = *state & bit == 0;
4403        *state |= bit;
4404        is_new
4405    }
4406}
4407
4408fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4409    if state.precedence_rule_decision()
4410        || !matches!(
4411            state.kind(),
4412            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4413        )
4414        || state.transitions().len() != 2
4415    {
4416        return None;
4417    }
4418    let mut enter = None;
4419    let mut exit = None;
4420    for (index, transition) in state.transitions().iter().enumerate() {
4421        if transition.kind() != ParserTransitionKind::Epsilon {
4422            return None;
4423        }
4424        let target = transition.target();
4425        if atn
4426            .state(target)
4427            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4428        {
4429            if exit.replace((index, target)).is_some() {
4430                return None;
4431            }
4432        } else if enter.replace((index, target)).is_some() {
4433            return None;
4434        }
4435    }
4436    let (enter_transition_index, enter_target) = enter?;
4437    let (exit_transition_index, exit_target) = exit?;
4438    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4439        atn.state(exit_target)?.loop_back_state()?
4440    } else {
4441        state.state_number()
4442    };
4443    Some(FastRepetitionShape {
4444        enter_target,
4445        exit_target,
4446        body_stop_state,
4447        enter_transition_index,
4448        exit_transition_index,
4449    })
4450}
4451
4452fn push_fast_repetition_work(
4453    work: &mut Vec<FastRepetitionWork>,
4454    shape: FastRepetitionShape,
4455    path: FastRepetitionPath,
4456    lookahead: Option<&DecisionLookahead>,
4457    symbol: i32,
4458) {
4459    // Match the normal recognizer's FIRST-set pruning before queueing work.
4460    // Ambiguous body paths still share the coordinate bitmap below.
4461    let transition_is_viable = |transition_index: usize| {
4462        let Some(entry) = lookahead else {
4463            return true;
4464        };
4465        let Some(transition) = entry.transitions.get(transition_index) else {
4466            return true;
4467        };
4468        transition.nullable || transition.symbols.contains(symbol)
4469    };
4470    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4471    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4472    if shape.enter_transition_index < shape.exit_transition_index {
4473        if exit_is_viable {
4474            work.push(FastRepetitionWork::Exit(path));
4475        }
4476        if enter_is_viable {
4477            work.push(FastRepetitionWork::Enter(path));
4478        }
4479    } else {
4480        if enter_is_viable {
4481            work.push(FastRepetitionWork::Enter(path));
4482        }
4483        if exit_is_viable {
4484            work.push(FastRepetitionWork::Exit(path));
4485        }
4486    }
4487}
4488
4489/// Memo key for the fast recognizer. `recovery_symbols` must come from
4490/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4491/// key, so equal sets share one allocation and the key can store that
4492/// allocation's address instead of cloning an `Rc` and walking the full
4493/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4494/// into distinct cache coordinates.
4495#[derive(Clone, Debug)]
4496struct FastRecognizeKey {
4497    state_number: usize,
4498    stop_state: usize,
4499    index: usize,
4500    rule_start_index: usize,
4501    decision_start_index: Option<usize>,
4502    precedence: i32,
4503    recovery_symbols_id: usize,
4504    recovery_state: Option<usize>,
4505}
4506
4507impl PartialEq for FastRecognizeKey {
4508    fn eq(&self, other: &Self) -> bool {
4509        if self.state_number != other.state_number
4510            || self.stop_state != other.stop_state
4511            || self.index != other.index
4512            || self.rule_start_index != other.rule_start_index
4513            || self.decision_start_index != other.decision_start_index
4514            || self.precedence != other.precedence
4515            || self.recovery_state != other.recovery_state
4516            || self.recovery_symbols_id != other.recovery_symbols_id
4517        {
4518            return false;
4519        }
4520        true
4521    }
4522}
4523
4524impl Eq for FastRecognizeKey {}
4525
4526impl Hash for FastRecognizeKey {
4527    fn hash<H: Hasher>(&self, hasher: &mut H) {
4528        self.state_number.hash(hasher);
4529        self.stop_state.hash(hasher);
4530        self.index.hash(hasher);
4531        self.rule_start_index.hash(hasher);
4532        self.decision_start_index.hash(hasher);
4533        self.precedence.hash(hasher);
4534        self.recovery_state.hash(hasher);
4535        self.recovery_symbols_id.hash(hasher);
4536    }
4537}
4538
4539struct FastRecoveryRequest<'a, 'b> {
4540    atn: &'a Atn,
4541    transition: ParserTransition<'a>,
4542    expected_symbols: Rc<BTreeSet<i32>>,
4543    target: usize,
4544    request: FastRecognizeRequest,
4545    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4546    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4547    expected: &'b mut ExpectedTokens,
4548}
4549
4550struct FastCurrentTokenDeletionRequest<'a, 'b> {
4551    atn: &'a Atn,
4552    expected_symbols: Rc<BTreeSet<i32>>,
4553    request: FastRecognizeRequest,
4554    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4555    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4556    expected: &'b mut ExpectedTokens,
4557}
4558
4559#[derive(Clone, Copy)]
4560struct FastChildRuleFailureRecoveryRequest<'a> {
4561    atn: &'a Atn,
4562    rule_index: usize,
4563    start_index: usize,
4564    follow_state: usize,
4565    stop_state: usize,
4566    expected: &'a ExpectedTokens,
4567}
4568
4569struct RecoveryRequest<'a, 'b> {
4570    atn: &'a Atn,
4571    transition: ParserTransition<'a>,
4572    expected_symbols: BTreeSet<i32>,
4573    target: usize,
4574    request: RecognizeRequest<'a>,
4575    visiting: &'b mut BTreeSet<RecognizeKey>,
4576    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4577    expected: &'b mut ExpectedTokens,
4578}
4579
4580struct CurrentTokenDeletionRequest<'a, 'b> {
4581    atn: &'a Atn,
4582    expected_symbols: BTreeSet<i32>,
4583    request: RecognizeRequest<'a>,
4584    visiting: &'b mut BTreeSet<RecognizeKey>,
4585    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4586    expected: &'b mut ExpectedTokens,
4587}
4588
4589/// Carries the state needed after the normal token-recovery strategies fail
4590/// for a consuming transition.
4591struct ConsumingFailureFallback<'a> {
4592    atn: &'a Atn,
4593    target: usize,
4594    request: RecognizeRequest<'a>,
4595    symbol: i32,
4596    expected_symbols: BTreeSet<i32>,
4597    decision_start_index: Option<usize>,
4598    decision: Option<usize>,
4599}
4600
4601/// Captures the parent-rule context needed when a called rule fails before it
4602/// can produce a normal outcome.
4603struct ChildRuleFailureRecovery<'a> {
4604    atn: &'a Atn,
4605    rule_index: usize,
4606    start_index: usize,
4607    follow_state: usize,
4608    stop_state: usize,
4609    member_values: MemberEnv,
4610    expected: &'a ExpectedTokens,
4611}
4612
4613/// Bundles the context needed to evaluate one semantic predicate transition.
4614#[derive(Clone, Copy, Debug)]
4615struct PredicateEval<'a> {
4616    index: usize,
4617    rule_index: usize,
4618    pred_index: usize,
4619    predicates: &'a [(usize, usize, ParserPredicate)],
4620    semantics: Option<&'a ParserSemantics>,
4621    context: Option<&'a ParserRuleContext>,
4622    local_int_arg: Option<(usize, i64)>,
4623    member_values: &'a MemberEnv,
4624}
4625
4626#[derive(Clone, Copy, Debug)]
4627struct ParserSemanticHookRequest<'a> {
4628    index: usize,
4629    rule_index: usize,
4630    pred_index: usize,
4631    context: Option<&'a ParserRuleContext>,
4632    local_int_arg: Option<(usize, i64)>,
4633    member_values: &'a MemberEnv,
4634}
4635
4636/// Predicate-evaluation context over the recognizer's speculative state.
4637///
4638/// This sits in the prediction hot loop, so everything is borrowed: member
4639/// state read-only from the current speculative path and the rule name
4640/// straight from recognizer metadata. Predicates are pure by construction
4641/// ([`semir::PExpr`] has no mutating node); statement execution uses
4642/// [`ParserTableSemCtx`] (speculative member/return replay) and
4643/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4644struct ParserSemIrCtx<'a, S, H>
4645where
4646    S: TokenSource,
4647    H: SemanticHooks,
4648{
4649    input: &'a mut CommonTokenStream<S>,
4650    tree_storage: &'a ParseTreeStorage,
4651    semantic_hooks: &'a mut H,
4652    rule_index: usize,
4653    coordinate_index: usize,
4654    rule_name: Option<&'a str>,
4655    context: Option<&'a ParserRuleContext>,
4656    local_int_arg: Option<(usize, i64)>,
4657    member_values: &'a MemberEnv,
4658    invoked_predicates: &'a mut Vec<(usize, usize)>,
4659    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4660    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4661    /// table path instead of coercing the miss to `false`.
4662    unknown_predicate_policy: UnknownSemanticPolicy,
4663    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4664}
4665
4666impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4667where
4668    S: TokenSource,
4669    H: SemanticHooks,
4670{
4671    type TokenText<'a>
4672        = TokenView<'a>
4673    where
4674        Self: 'a;
4675
4676    fn la(&mut self, offset: isize) -> i64 {
4677        i64::from(self.input.la(offset))
4678    }
4679
4680    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4681        self.input.lt(offset)
4682    }
4683
4684    fn token_index_adjacent(&mut self) -> bool {
4685        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4686            return false;
4687        };
4688        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4689            return false;
4690        };
4691        first + 1 == second
4692    }
4693
4694    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4695        self.context.and_then(|context| {
4696            context
4697                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4698                .next()
4699                .map(crate::tree::RuleNodeView::text)
4700        })
4701    }
4702
4703    fn member(&self, member: usize) -> Option<i64> {
4704        Some(self.member_values.scalar(member).unwrap_or_default())
4705    }
4706
4707    fn member_top(&self, member: usize) -> Option<i64> {
4708        self.member_values.stack_top(member)
4709    }
4710
4711    fn member_len(&self, member: usize) -> usize {
4712        self.member_values.stack_len(member)
4713    }
4714
4715    fn local_arg(&self) -> Option<i64> {
4716        self.local_int_arg.map(|(_, value)| value)
4717    }
4718
4719    fn column(&self) -> Option<i64> {
4720        None
4721    }
4722
4723    fn token_start_column(&self) -> Option<i64> {
4724        None
4725    }
4726
4727    fn token_text_so_far(&self) -> Option<String> {
4728        None
4729    }
4730
4731    fn hook(&mut self, _hook: HookId) -> bool {
4732        let mut ctx = ParserSemCtx {
4733            input: &mut *self.input,
4734            tree_storage: self.tree_storage,
4735            rule_index: self.rule_index,
4736            coordinate_index: self.coordinate_index,
4737            rule_name: self.rule_name.map(str::to_owned),
4738            context: self.context,
4739            tree: None,
4740            local_int_arg: self.local_int_arg,
4741            member_values: self.member_values,
4742            action: None,
4743        };
4744        match self
4745            .semantic_hooks
4746            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4747        {
4748            Some(result) => result,
4749            // No hook answered this coordinate: fall through to the configured
4750            // policy instead of silently rejecting the alternative, matching the
4751            // legacy table path's dispatch chain (hook → policy).
4752            None => apply_unknown_predicate_policy(
4753                self.unknown_predicate_policy,
4754                self.rule_index,
4755                self.coordinate_index,
4756                self.unknown_predicate_hits,
4757            ),
4758        }
4759    }
4760
4761    fn trace_bool(&mut self, value: bool) -> bool {
4762        let key = (self.rule_index, self.coordinate_index);
4763        if !self.invoked_predicates.contains(&key) {
4764            self.invoked_predicates.push(key);
4765            use std::io::Write as _;
4766            let mut stdout = std::io::stdout().lock();
4767            let _ = writeln!(stdout, "eval={value}");
4768        }
4769        value
4770    }
4771}
4772
4773/// Captures predicate-failure recovery metadata for fail-option predicates.
4774struct PredicateFailureRecovery<'a> {
4775    rule_index: usize,
4776    index: usize,
4777    message: &'a str,
4778    member_values: MemberEnv,
4779    return_values: BTreeMap<String, i64>,
4780    rule_alt_number: usize,
4781}
4782
4783#[derive(Debug)]
4784enum DirectAdaptiveParseControl {
4785    Fallback(DirectAdaptiveFallback),
4786}
4787
4788#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4789enum DirectAdaptiveFallback {
4790    Action,
4791    InvalidAlt,
4792    LeftRecursiveBoundary,
4793    MissingAtn,
4794    NoTransition,
4795    Predicate,
4796    Prediction,
4797    Precedence,
4798    RuleStop,
4799    SemanticContext,
4800    StepLimit,
4801    TokenMismatch,
4802    UnknownDecision,
4803}
4804
4805type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
4806
4807struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
4808where
4809    S: TokenSource,
4810    H: SemanticHooks,
4811{
4812    parser: &'sim mut BaseParser<S, H>,
4813    atn: &'atn Atn,
4814    simulator: &'sim mut ParserAtnSimulator<'atn>,
4815    decision_by_state: Vec<Option<usize>>,
4816    steps: usize,
4817}
4818
4819/// Outcome of a generated token / set / not-set match that may recover.
4820///
4821/// Generated parsers append `children` to the current rule context. `consumed_eof`
4822/// reports whether the match actually consumed a real EOF terminal — it is true
4823/// only on a successful match (or single-token deletion that lands on EOF), and
4824/// always false on single-token insertion, which synthesizes a missing token and
4825/// consumes nothing. Generated code feeds this into `finish_rule`'s
4826/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
4827/// truly matched, matching ANTLR's `matchedEOF` semantics.
4828#[derive(Clone, Debug, Eq, PartialEq)]
4829pub struct GeneratedMatch {
4830    children: GeneratedMatchChildren,
4831    consumed_eof: bool,
4832}
4833
4834#[derive(Clone, Copy)]
4835enum GeneratedExpectedSymbols<'a> {
4836    Tree(&'a BTreeSet<i32>),
4837    TokenSet(ParserIntervalSet<'a>),
4838    TokenSetComplement {
4839        set: ParserIntervalSet<'a>,
4840        min_vocabulary: i32,
4841        max_vocabulary: i32,
4842    },
4843}
4844
4845impl GeneratedExpectedSymbols<'_> {
4846    fn is_empty(self) -> bool {
4847        match self {
4848            Self::Tree(symbols) => symbols.is_empty(),
4849            Self::TokenSet(set) => set.is_empty(),
4850            Self::TokenSetComplement {
4851                set,
4852                min_vocabulary,
4853                max_vocabulary,
4854            } => (min_vocabulary..=max_vocabulary).all(|symbol| set.contains(symbol)),
4855        }
4856    }
4857
4858    fn first(self) -> Option<i32> {
4859        match self {
4860            Self::Tree(symbols) => symbols.iter().next().copied(),
4861            Self::TokenSet(set) => set.ranges().next().map(|(start, _)| start),
4862            Self::TokenSetComplement {
4863                set,
4864                min_vocabulary,
4865                max_vocabulary,
4866            } => (min_vocabulary..=max_vocabulary).find(|symbol| !set.contains(*symbol)),
4867        }
4868    }
4869
4870    fn display(self, vocabulary: &Vocabulary) -> String {
4871        match self {
4872            Self::Tree(symbols) => expected_symbols_display(symbols, vocabulary),
4873            Self::TokenSet(set) => expected_symbols_display_iter(
4874                set.ranges().flat_map(|(start, stop)| start..=stop),
4875                vocabulary,
4876            ),
4877            Self::TokenSetComplement {
4878                set,
4879                min_vocabulary,
4880                max_vocabulary,
4881            } => expected_symbols_display_iter(
4882                (min_vocabulary..=max_vocabulary).filter(|symbol| !set.contains(*symbol)),
4883                vocabulary,
4884            ),
4885        }
4886    }
4887}
4888
4889#[derive(Clone, Debug, Eq, PartialEq)]
4890enum GeneratedMatchChildren {
4891    One(ParseTree),
4892    Many(Vec<ParseTree>),
4893}
4894
4895struct GeneratedMatchChildrenIntoIter {
4896    one: Option<ParseTree>,
4897    many: Option<std::vec::IntoIter<ParseTree>>,
4898}
4899
4900impl Iterator for GeneratedMatchChildrenIntoIter {
4901    type Item = ParseTree;
4902
4903    fn next(&mut self) -> Option<Self::Item> {
4904        self.one
4905            .take()
4906            .or_else(|| self.many.as_mut().and_then(Iterator::next))
4907    }
4908}
4909
4910impl GeneratedMatch {
4911    /// Parse-tree children produced by the match (the matched terminal, an
4912    /// error node plus deleted-then-matched terminal, or a single missing-token
4913    /// error node).
4914    #[must_use]
4915    pub fn children(&self) -> &[ParseTree] {
4916        match &self.children {
4917            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
4918            GeneratedMatchChildren::Many(children) => children,
4919        }
4920    }
4921
4922    /// Consumes the result, returning the children for appending to the rule
4923    /// context.
4924    #[must_use]
4925    pub fn into_children(self) -> Vec<ParseTree> {
4926        match self.children {
4927            GeneratedMatchChildren::One(child) => vec![child],
4928            GeneratedMatchChildren::Many(children) => children,
4929        }
4930    }
4931
4932    /// Consumes the match without allocating for the common single-child case.
4933    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
4934        match self.children {
4935            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
4936                one: Some(child),
4937                many: None,
4938            },
4939            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
4940                one: None,
4941                many: Some(children.into_iter()),
4942            },
4943        }
4944    }
4945
4946    /// Whether a real EOF terminal was consumed by this match.
4947    #[must_use]
4948    pub const fn consumed_eof(&self) -> bool {
4949        self.consumed_eof
4950    }
4951}
4952
4953impl<S> BaseParser<S, NoSemanticHooks>
4954where
4955    S: TokenSource,
4956{
4957    /// Creates a parser base over a buffered token stream and recognizer
4958    /// metadata.
4959    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
4960        Self::with_semantic_hooks(input, data, NoSemanticHooks)
4961    }
4962}
4963
4964impl<S, H> BaseParser<S, H>
4965where
4966    S: TokenSource,
4967    H: SemanticHooks,
4968{
4969    /// Creates a parser base with caller-owned semantic hooks.
4970    pub fn with_semantic_hooks(
4971        input: CommonTokenStream<S>,
4972        data: RecognizerData,
4973        semantic_hooks: H,
4974    ) -> Self {
4975        Self {
4976            input,
4977            tree: ParseTreeStorage::new(),
4978            data,
4979            semantic_hooks,
4980            decision_override_generation: 0,
4981            build_parse_trees: true,
4982            syntax_errors: 0,
4983            report_diagnostic_errors: false,
4984            prediction_mode: PredictionMode::Ll,
4985            prediction_diagnostics: Vec::new(),
4986            reported_prediction_diagnostics: BTreeSet::new(),
4987            generated_parser_diagnostics: Vec::new(),
4988            generated_sync_expected: None,
4989            generated_recovery_error_index: None,
4990            generated_recovery_error_states: BTreeSet::new(),
4991            int_members: MemberEnv::new(),
4992            rule_context_stack: Vec::new(),
4993            rule_context_version: 0,
4994            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
4995            pending_invoking_states: Vec::new(),
4996            precedence_stack: vec![0],
4997            invoked_predicates: Vec::new(),
4998            bail_on_error: false,
4999            parse_listeners: Vec::new(),
5000            parse_listener_abort: None,
5001            max_rule_depth: None,
5002            rule_depth_error: None,
5003            recursion_expansions: 0,
5004            recursion_expansion_marks: Vec::new(),
5005            unknown_predicate_policy: UnknownSemanticPolicy::default(),
5006            unknown_predicate_hits: Vec::new(),
5007            unhandled_action_hits: Vec::new(),
5008            rule_first_set_cache: Vec::new(),
5009            state_expected_cache: FxHashMap::default(),
5010            state_expected_token_cache: FxHashMap::default(),
5011            rule_stop_reach_cache: Vec::new(),
5012            recovery_symbols_intern: FxHashMap::default(),
5013            decision_lookahead_cache: FxHashMap::default(),
5014            ll1_decision_cache: FxHashMap::default(),
5015            fast_predicate_cache: FxHashMap::default(),
5016            empty_cycle_cache: Vec::new(),
5017            empty_cycle_cache_atn: None,
5018            clean_memo_mode: CleanMemoMode::Probe,
5019            clean_memo_probe_seen: FxHashSet::default(),
5020            clean_memo_probe_samples: 0,
5021            clean_memo_probe_repeats: 0,
5022            clean_memo_sparse_samples: 0,
5023            fast_recognize_scratch: FastRecognizeTopScratch::default(),
5024            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
5025            empty_recovery_symbols: Rc::new(BTreeSet::new()),
5026            fast_first_set_prefilter: true,
5027            fast_recovery_enabled: true,
5028            fast_token_nodes_enabled: true,
5029            fast_track_alt_numbers: false,
5030            recognition_arena: RecognitionArena::default(),
5031            last_recognition_arena_root: NodeSeqId::EMPTY,
5032            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
5033        }
5034    }
5035
5036    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
5037        &mut self.input
5038    }
5039
5040    /// Fully resets parser-owned state and rewinds the current token stream.
5041    ///
5042    /// Parser configuration, semantic hooks, learned DFA tables, and
5043    /// grammar-owned member values are retained.
5044    pub fn reset(&mut self) {
5045        self.input.seek(0);
5046        self.tree.reset();
5047        self.data.set_state(-1);
5048        self.syntax_errors = 0;
5049        self.prediction_diagnostics.clear();
5050        self.reported_prediction_diagnostics.clear();
5051        self.generated_parser_diagnostics.clear();
5052        self.generated_sync_expected = None;
5053        self.reset_generated_recovery_state();
5054        self.rule_context_stack.clear();
5055        self.advance_rule_context_version();
5056        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
5057        self.pending_invoking_states.clear();
5058        self.precedence_stack.clear();
5059        self.precedence_stack.push(0);
5060        self.invoked_predicates.clear();
5061        self.decision_override_generation = 0;
5062        self.unknown_predicate_hits.clear();
5063        self.unhandled_action_hits.clear();
5064        self.parse_listener_abort = None;
5065        self.rule_depth_error = None;
5066        self.recursion_expansions = 0;
5067        self.recursion_expansion_marks.clear();
5068        self.reset_per_parse_caches();
5069        self.fast_first_set_prefilter = true;
5070        self.fast_recovery_enabled = true;
5071        self.fast_token_nodes_enabled = self.build_parse_trees;
5072        self.fast_track_alt_numbers = false;
5073        self.reset_recognition_arena();
5074    }
5075
5076    /// Replaces the buffered token stream and fully resets this parser.
5077    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
5078        self.input = input;
5079        self.reset();
5080    }
5081
5082    /// Installs the policy for predicate coordinates that no translated table
5083    /// entry or user hook resolves.
5084    ///
5085    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
5086    /// but generated recursive-descent rules evaluate predicates directly
5087    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
5088    /// going through those options. Generated parser constructors call this so
5089    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
5090    /// the field at its `AssumeTrue` default and silently accepting an
5091    /// unimplemented hook predicate.
5092    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
5093        self.unknown_predicate_policy = policy;
5094    }
5095
5096    /// Reports any unknown predicate coordinate the generated-direct path
5097    /// recorded under [`UnknownSemanticPolicy::Error`], as an
5098    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
5099    /// after a rule completes so the fail-loud policy surfaces on the
5100    /// generated path the same way the interpreter entry surfaces it.
5101    #[must_use]
5102    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
5103        let error = self.unknown_semantic_error();
5104        self.unknown_predicate_hits.clear();
5105        self.unhandled_action_hits.clear();
5106        error
5107    }
5108
5109    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
5110    ///
5111    /// Generated parsers call this at the true top-level entry so a parser
5112    /// reused after a fail-loud (or recovered) parse starts clean, without
5113    /// clearing hits mid-parse where a generated parent still needs a child's
5114    /// recorded coordinate to survive to the top-level boundary.
5115    pub fn reset_unknown_semantic_hits(&mut self) {
5116        self.unknown_predicate_hits.clear();
5117        self.unhandled_action_hits.clear();
5118    }
5119
5120    /// Returns the token stream owned by this parser.
5121    #[must_use]
5122    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
5123        &self.input
5124    }
5125
5126    /// Returns the token stream for source replacement or in-place re-feeding.
5127    #[must_use]
5128    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
5129        &mut self.input
5130    }
5131
5132    /// Returns the canonical token store referenced by parse trees.
5133    #[must_use]
5134    pub const fn token_store(&self) -> &TokenStore {
5135        self.input.token_store()
5136    }
5137
5138    /// Returns the flat CST storage populated by completed rules.
5139    #[must_use]
5140    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
5141        &self.tree
5142    }
5143
5144    /// Resolves a compact parse-tree ID into a borrowing node view.
5145    #[must_use]
5146    pub fn node(&self, id: NodeId) -> Node<'_> {
5147        self.tree
5148            .node(self.input.token_store(), id)
5149            .expect("parser-produced node ID should remain valid")
5150    }
5151
5152    /// Consumes this parser and returns its token stream.
5153    #[must_use]
5154    pub fn into_token_stream(self) -> CommonTokenStream<S> {
5155        self.input
5156    }
5157
5158    /// Consumes this parser and returns its canonical token store.
5159    #[must_use]
5160    pub fn into_token_store(self) -> TokenStore {
5161        self.input.into_token_store()
5162    }
5163
5164    /// Consumes the parser and pairs its token store and flat CST with `root`.
5165    #[must_use]
5166    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
5167        ParsedFile::new(self.input.into_token_store(), self.tree, root)
5168    }
5169
5170    /// Returns the number of parser syntax errors recorded by committed parse
5171    /// paths so far.
5172    pub const fn number_of_syntax_errors(&self) -> usize {
5173        self.syntax_errors
5174    }
5175
5176    /// Computes reachability and retained-capacity counters for the most recent
5177    /// interpreted-rule recognition arena.
5178    ///
5179    /// The reachability scan is linear in the arena size and is deferred until
5180    /// this instrumentation method is called.
5181    #[must_use]
5182    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
5183        self.recognition_arena.stats(
5184            self.last_recognition_arena_root,
5185            self.last_recognition_arena_diagnostics,
5186        )
5187    }
5188
5189    /// Records a syntax error that generated parser code returns as fatal before
5190    /// it can recover into the current rule context.
5191    pub const fn record_generated_syntax_error(&mut self) {
5192        self.record_syntax_errors(1);
5193    }
5194
5195    const fn record_syntax_errors(&mut self, count: usize) {
5196        self.syntax_errors = self.syntax_errors.saturating_add(count);
5197    }
5198
5199    /// Returns whether no interpreted rule context or generated invocation is active.
5200    const fn is_top_level_entry(&self) -> bool {
5201        self.rule_context_stack.is_empty() && self.pending_invoking_states.is_empty()
5202    }
5203
5204    /// Emits diagnostics buffered by the token stream while generated parser
5205    /// code was fetching lexer tokens directly.
5206    pub fn report_token_source_errors(&mut self) {
5207        let errors = self.input.drain_source_errors();
5208        self.dispatch_token_source_errors(&errors);
5209    }
5210
5211    /// Captures generated-parser diagnostics and syntax-error count before a
5212    /// speculative generated rule path.
5213    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
5214        GeneratedDiagnosticsCheckpoint {
5215            diagnostics_len: self.generated_parser_diagnostics.len(),
5216            syntax_errors: self.syntax_errors,
5217            tree: self.tree.checkpoint(),
5218        }
5219    }
5220
5221    /// Restores generated-parser diagnostics after a speculative rule path failed.
5222    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
5223        self.generated_parser_diagnostics
5224            .truncate(marker.diagnostics_len);
5225        self.syntax_errors = marker.syntax_errors;
5226        self.rollback_generated_tree(marker);
5227    }
5228
5229    /// Rolls back generated tree state while retaining committed diagnostics.
5230    ///
5231    /// Fatal public entries use this after an earlier child recovery: the
5232    /// partial tree is discarded, but ANTLR has already committed the child's
5233    /// diagnostic and syntax-error count.
5234    pub fn rollback_generated_tree(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
5235        self.generated_sync_expected = None;
5236        self.tree.rollback(marker.tree);
5237    }
5238
5239    /// Emits diagnostics recorded by committed generated parser recovery.
5240    pub fn report_generated_parser_diagnostics(&mut self) {
5241        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
5242        let token_errors = self.input.drain_source_errors();
5243        self.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
5244    }
5245
5246    fn syntax_error_event<'a>(
5247        &'a self,
5248        offending: Option<TokenId>,
5249        line: usize,
5250        column: usize,
5251        message: &'a str,
5252        error: Option<&'a AntlrError>,
5253    ) -> SyntaxErrorEvent<'a> {
5254        let offending = offending.and_then(|token| self.token_store().view(token));
5255        SyntaxErrorEvent {
5256            offending,
5257            line,
5258            column,
5259            span: offending.and_then(|token| token.byte_span()),
5260            message,
5261            error,
5262        }
5263    }
5264
5265    /// Emits a fatal parser error after an entry-rule parse commits to returning it.
5266    ///
5267    /// Generated parsers call this only at their public entry boundary. Nested
5268    /// failures remain silent until generated recovery commits and buffers them.
5269    pub fn report_unrecovered_parser_error(&self, error: &AntlrError) {
5270        let AntlrError::ParserError {
5271            line,
5272            column,
5273            message,
5274            offending,
5275        } = error
5276        else {
5277            return;
5278        };
5279        self.notify_error_listeners(self.syntax_error_event(
5280            *offending,
5281            *line,
5282            *column,
5283            message,
5284            Some(error),
5285        ));
5286    }
5287
5288    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
5289        self.notify_error_listeners(self.syntax_error_event(
5290            diagnostic.offending,
5291            diagnostic.line,
5292            diagnostic.column,
5293            &diagnostic.message,
5294            None,
5295        ));
5296    }
5297
5298    fn dispatch_parser_diagnostics<'a>(
5299        &self,
5300        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
5301    ) {
5302        for diagnostic in diagnostics {
5303            self.dispatch_parser_diagnostic(diagnostic);
5304        }
5305    }
5306
5307    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
5308        if self.input.token_source().report_error(source_error) {
5309            return;
5310        }
5311        // Lexer errors have no offending token: the failure is that no token
5312        // could be produced, matching ANTLR's null offendingSymbol.
5313        self.notify_error_listeners(source_error.into());
5314    }
5315
5316    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
5317        for error in errors {
5318            self.dispatch_token_source_error(error);
5319        }
5320    }
5321
5322    /// Dispatches generated parser and lexer diagnostics in the same
5323    /// source-position order as ANTLR's lazy token stream reports them.
5324    fn dispatch_generated_diagnostics(
5325        &self,
5326        parser_diagnostics: &[ParserDiagnostic],
5327        token_errors: &[TokenSourceError],
5328    ) {
5329        // Parser diagnostics keep their event order: Java's console and
5330        // DiagnosticErrorListener print reports as prediction produces them,
5331        // so reportAttemptingFullContext precedes reportContextSensitivity
5332        // even though the latter's position is earlier. Buffered token-source
5333        // errors interleave by source position and win ties.
5334        let mut token_iter = token_errors.iter().peekable();
5335        for diagnostic in parser_diagnostics {
5336            while let Some(error) = token_iter.peek() {
5337                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
5338                    self.dispatch_token_source_error(error);
5339                    token_iter.next();
5340                } else {
5341                    break;
5342                }
5343            }
5344            self.dispatch_parser_diagnostic(diagnostic);
5345        }
5346        for error in token_iter {
5347            self.dispatch_token_source_error(error);
5348        }
5349    }
5350
5351    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
5352    /// decision code.
5353    pub fn record_generated_ambiguity_diagnostic(
5354        &mut self,
5355        atn: &Atn,
5356        state_number: usize,
5357        start_index: usize,
5358        stop_index: usize,
5359        alts: &[usize],
5360    ) {
5361        if !self.report_diagnostic_errors || alts.len() < 2 {
5362            return;
5363        }
5364        let Some(decision) = atn
5365            .decision_to_state()
5366            .iter()
5367            .position(|candidate| candidate == state_number)
5368        else {
5369            return;
5370        };
5371        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5372            return;
5373        };
5374        let rule_name = self
5375            .rule_names()
5376            .get(rule_index)
5377            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5378        let input = display_input_text(&self.input.text(start_index, stop_index));
5379        let alts = alts
5380            .iter()
5381            .map(usize::to_string)
5382            .collect::<Vec<_>>()
5383            .join(", ");
5384        let key = (decision, start_index, format!("{alts}:{input}"));
5385        if !self.reported_prediction_diagnostics.insert(key) {
5386            return;
5387        }
5388        let start_diagnostic = diagnostic_for_token(
5389            self.token_at(start_index),
5390            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5391        );
5392        let stop_diagnostic = diagnostic_for_token(
5393            self.token_at(stop_index),
5394            format!(
5395                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5396            ),
5397        );
5398        self.generated_parser_diagnostics.push(start_diagnostic);
5399        self.generated_parser_diagnostics.push(stop_diagnostic);
5400    }
5401
5402    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5403    /// parser calls to the adaptive simulator.
5404    pub fn record_generated_prediction_diagnostic(
5405        &mut self,
5406        atn: &Atn,
5407        state_number: usize,
5408        prediction: &ParserAtnPrediction,
5409    ) {
5410        let Some(diagnostic) = &prediction.diagnostic else {
5411            return;
5412        };
5413        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5414            return;
5415        }
5416        let Some(decision) = atn
5417            .decision_to_state()
5418            .iter()
5419            .position(|candidate| candidate == state_number)
5420        else {
5421            return;
5422        };
5423        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5424            return;
5425        };
5426        let rule_name = self
5427            .rule_names()
5428            .get(rule_index)
5429            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5430        let attempt_input = display_input_text(
5431            &self
5432                .input
5433                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5434        );
5435        let result_input = display_input_text(
5436            &self
5437                .input
5438                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5439        );
5440        let alts = diagnostic
5441            .conflicting_alts
5442            .iter()
5443            .map(usize::to_string)
5444            .collect::<Vec<_>>()
5445            .join(", ");
5446        let key = (
5447            decision,
5448            diagnostic.start_index,
5449            format!(
5450                "{:?}:{alts}:{attempt_input}:{result_input}",
5451                diagnostic.kind
5452            ),
5453        );
5454        if !self.reported_prediction_diagnostics.insert(key) {
5455            return;
5456        }
5457        let attempt_diagnostic = diagnostic_for_token(
5458            self.token_at(diagnostic.sll_stop_index),
5459            format!(
5460                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5461            ),
5462        );
5463        self.generated_parser_diagnostics.push(attempt_diagnostic);
5464        let message = match diagnostic.kind {
5465            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5466                // Java's DiagnosticErrorListener is exactOnly by default:
5467                // non-exact ambiguities (default LL mode stopping at the
5468                // first resolvable conflict) report the attempt above but
5469                // suppress the ambiguity line itself.
5470                if !diagnostic.exact {
5471                    return;
5472                }
5473                format!(
5474                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5475                )
5476            }
5477            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5478                format!(
5479                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5480                )
5481            }
5482        };
5483        let result_diagnostic =
5484            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5485        self.generated_parser_diagnostics.push(result_diagnostic);
5486    }
5487
5488    pub fn la(&self, offset: isize) -> i32 {
5489        self.input.la_token(offset)
5490    }
5491
5492    pub fn consume(&mut self) {
5493        IntStream::consume(&mut self.input);
5494    }
5495
5496    /// Sets a generated integer member value used by target-template tests.
5497    pub fn set_int_member(&mut self, member: usize, value: i64) {
5498        self.int_members.set_scalar(member, value);
5499    }
5500
5501    /// Reads a generated integer member value.
5502    pub fn int_member(&self, member: usize) -> Option<i64> {
5503        self.int_members.scalar(member)
5504    }
5505
5506    /// Pushes onto a generated stack-valued member slot (issue #206).
5507    pub fn push_stack_member(&mut self, member: usize, value: i64) {
5508        self.int_members.push_stack(member, value);
5509    }
5510
5511    /// Pops a generated stack-valued member slot, returning the removed value.
5512    /// `None` when the stack is empty.
5513    pub fn pop_stack_member(&mut self, member: usize) -> Option<i64> {
5514        self.int_members.pop_stack(member)
5515    }
5516
5517    /// Reads the top of a generated stack-valued member slot; `None` when
5518    /// empty or never pushed.
5519    #[must_use]
5520    pub fn stack_member_top(&self, member: usize) -> Option<i64> {
5521        self.int_members.stack_top(member)
5522    }
5523
5524    /// Depth of a generated stack-valued member slot.
5525    #[must_use]
5526    pub fn stack_member_len(&self, member: usize) -> usize {
5527        self.int_members.stack_len(member)
5528    }
5529
5530    /// Seeds grammar-declared initial member values (issue #206).
5531    ///
5532    /// Generated parsers call this at construction for a grammar whose
5533    /// `@members` declares an initializer (`private int level = 1;`). Without
5534    /// it the slot would start at 0, so a predicate reading it would reject
5535    /// input the source grammar accepts.
5536    pub fn set_initial_members(&mut self, initial: impl IntoIterator<Item = (usize, i64)>) {
5537        self.int_members = MemberEnv::with_initial_scalars(initial);
5538    }
5539
5540    /// Captures generated member state before speculative generated parser
5541    /// execution.
5542    ///
5543    /// The snapshot covers scalar *and* stack slots: restoring only scalars
5544    /// would leave a rolled-back path's pushes behind.
5545    #[must_use]
5546    pub fn int_members_checkpoint(&self) -> MemberEnv {
5547        self.int_members.clone()
5548    }
5549
5550    /// Restores generated member state after generated parser fallback.
5551    pub fn restore_int_members(&mut self, members: MemberEnv) {
5552        self.int_members = members;
5553    }
5554
5555    /// Adds `delta` to a generated integer member and returns the new value.
5556    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5557        self.int_members.add_scalar(member, delta)
5558    }
5559
5560    fn token_type_for_id(&self, id: TokenId) -> i32 {
5561        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5562    }
5563
5564    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5565        if self.build_parse_trees {
5566            self.tree.terminal(id)
5567        } else {
5568            NodeId::placeholder()
5569        }
5570    }
5571
5572    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5573        if self.build_parse_trees {
5574            self.tree.error(id)
5575        } else {
5576            NodeId::placeholder()
5577        }
5578    }
5579
5580    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5581        context.set_start_id(id);
5582    }
5583
5584    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5585        context.set_stop_id(id);
5586    }
5587
5588    fn insert_synthetic_token(
5589        &mut self,
5590        token_type: i32,
5591        text: String,
5592        line: usize,
5593        column: usize,
5594    ) -> Result<TokenId, AntlrError> {
5595        self.input
5596            .insert(
5597                TokenSpec::explicit(token_type, text)
5598                    .with_span(usize::MAX, usize::MAX)
5599                    .with_position(line, column),
5600            )
5601            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5602    }
5603
5604    /// Matches and consumes the current token when it has the expected token
5605    /// type.
5606    ///
5607    /// On success the consumed token is wrapped as a terminal parse-tree node.
5608    /// On mismatch the error carries vocabulary display names so diagnostics are
5609    /// stable across literal and symbolic token naming.
5610    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5611        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5612            line: 0,
5613            column: 0,
5614            message: "missing current token".to_owned(),
5615            offending: None,
5616        })?;
5617        let current_type = self.token_type_for_id(current);
5618        if current_type == token_type {
5619            self.reset_generated_recovery_state();
5620            self.consume();
5621            Ok(self.terminal_tree(current))
5622        } else {
5623            Err(AntlrError::MismatchedInput {
5624                expected: self.vocabulary().display_name(token_type),
5625                found: self.vocabulary().display_name(current_type),
5626            })
5627        }
5628    }
5629
5630    /// Matches a token from generated recursive-descent code, including ANTLR's
5631    /// single-token insertion recovery when the active rule context can legally
5632    /// continue at the current input symbol.
5633    pub fn match_token_recovering(
5634        &mut self,
5635        token_type: i32,
5636        follow_state: usize,
5637        atn: &Atn,
5638    ) -> Result<GeneratedMatch, AntlrError> {
5639        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5640            line: 0,
5641            column: 0,
5642            message: "missing current token".to_owned(),
5643            offending: None,
5644        })?;
5645        let current_type = self.token_type_for_id(current);
5646        if current_type == token_type {
5647            self.generated_sync_expected = None;
5648            self.reset_generated_recovery_state();
5649            let consumed_eof = current_type == TOKEN_EOF;
5650            self.consume();
5651            return Ok(GeneratedMatch {
5652                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5653                consumed_eof,
5654            });
5655        }
5656        let mut expected_symbols = BTreeSet::new();
5657        expected_symbols.insert(token_type);
5658        self.recover_generated_match(
5659            current,
5660            GeneratedExpectedSymbols::Tree(&expected_symbols),
5661            follow_state,
5662            atn,
5663            |symbol| symbol == token_type,
5664        )
5665    }
5666
5667    pub fn match_set_recovering(
5668        &mut self,
5669        intervals: &[(i32, i32)],
5670        follow_state: usize,
5671        atn: &Atn,
5672    ) -> Result<GeneratedMatch, AntlrError> {
5673        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5674            line: 0,
5675            column: 0,
5676            message: "missing current token".to_owned(),
5677            offending: None,
5678        })?;
5679        let current_type = self.token_type_for_id(current);
5680        if interval_set_contains(intervals, current_type) {
5681            self.generated_sync_expected = None;
5682            self.reset_generated_recovery_state();
5683            let consumed_eof = current_type == TOKEN_EOF;
5684            self.consume();
5685            return Ok(GeneratedMatch {
5686                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5687                consumed_eof,
5688            });
5689        }
5690        let expected_symbols = interval_symbols(intervals);
5691        self.recover_generated_match(
5692            current,
5693            GeneratedExpectedSymbols::Tree(&expected_symbols),
5694            follow_state,
5695            atn,
5696            |symbol| interval_set_contains(intervals, symbol),
5697        )
5698    }
5699
5700    pub fn match_token_set_recovering(
5701        &mut self,
5702        set: ParserIntervalSet<'_>,
5703        follow_state: usize,
5704        atn: &Atn,
5705    ) -> Result<GeneratedMatch, AntlrError> {
5706        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5707            line: 0,
5708            column: 0,
5709            message: "missing current token".to_owned(),
5710            offending: None,
5711        })?;
5712        let current_type = self.token_type_for_id(current);
5713        if set.contains(current_type) {
5714            self.generated_sync_expected = None;
5715            self.reset_generated_recovery_state();
5716            let consumed_eof = current_type == TOKEN_EOF;
5717            self.consume();
5718            return Ok(GeneratedMatch {
5719                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5720                consumed_eof,
5721            });
5722        }
5723        self.recover_generated_match(
5724            current,
5725            GeneratedExpectedSymbols::TokenSet(set),
5726            follow_state,
5727            atn,
5728            |symbol| set.contains(symbol),
5729        )
5730    }
5731
5732    pub fn match_not_set_recovering(
5733        &mut self,
5734        intervals: &[(i32, i32)],
5735        min_vocabulary: i32,
5736        max_vocabulary: i32,
5737        follow_state: usize,
5738        atn: &Atn,
5739    ) -> Result<GeneratedMatch, AntlrError> {
5740        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5741            line: 0,
5742            column: 0,
5743            message: "missing current token".to_owned(),
5744            offending: None,
5745        })?;
5746        let current_type = self.token_type_for_id(current);
5747        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5748            && !interval_set_contains(intervals, current_type)
5749        {
5750            self.generated_sync_expected = None;
5751            self.reset_generated_recovery_state();
5752            let consumed_eof = current_type == TOKEN_EOF;
5753            self.consume();
5754            return Ok(GeneratedMatch {
5755                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5756                consumed_eof,
5757            });
5758        }
5759        let expected_symbols =
5760            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5761        self.recover_generated_match(
5762            current,
5763            GeneratedExpectedSymbols::Tree(&expected_symbols),
5764            follow_state,
5765            atn,
5766            |symbol| {
5767                (min_vocabulary..=max_vocabulary).contains(&symbol)
5768                    && !interval_set_contains(intervals, symbol)
5769            },
5770        )
5771    }
5772
5773    pub fn match_not_token_set_recovering(
5774        &mut self,
5775        set: ParserIntervalSet<'_>,
5776        min_vocabulary: i32,
5777        max_vocabulary: i32,
5778        follow_state: usize,
5779        atn: &Atn,
5780    ) -> Result<GeneratedMatch, AntlrError> {
5781        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5782            line: 0,
5783            column: 0,
5784            message: "missing current token".to_owned(),
5785            offending: None,
5786        })?;
5787        let current_type = self.token_type_for_id(current);
5788        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
5789        {
5790            self.generated_sync_expected = None;
5791            self.reset_generated_recovery_state();
5792            let consumed_eof = current_type == TOKEN_EOF;
5793            self.consume();
5794            return Ok(GeneratedMatch {
5795                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5796                consumed_eof,
5797            });
5798        }
5799        self.recover_generated_match(
5800            current,
5801            GeneratedExpectedSymbols::TokenSetComplement {
5802                set,
5803                min_vocabulary,
5804                max_vocabulary,
5805            },
5806            follow_state,
5807            atn,
5808            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
5809        )
5810    }
5811
5812    fn recover_generated_match(
5813        &mut self,
5814        current: TokenId,
5815        expected_symbols: GeneratedExpectedSymbols<'_>,
5816        follow_state: usize,
5817        atn: &Atn,
5818        matches: impl Fn(i32) -> bool,
5819    ) -> Result<GeneratedMatch, AntlrError> {
5820        let expected_display = expected_symbols.display(self.vocabulary());
5821        let (current_type, current_line, current_column, current_display) = {
5822            let token = self
5823                .input
5824                .token_view(current)
5825                .expect("current token ID should be valid");
5826            (
5827                token.token_type(),
5828                token.line(),
5829                token.column(),
5830                token_input_display(&token),
5831            )
5832        };
5833        if self.bail_on_error {
5834            return Err(AntlrError::ParserError {
5835                line: current_line,
5836                column: current_column,
5837                message: format!("mismatched input {current_display} expecting {expected_display}"),
5838                offending: Some(current),
5839            });
5840        }
5841        if current_type != TOKEN_EOF
5842            && let Some(next) = self.input.lt_id(2)
5843            && matches(self.token_type_for_id(next))
5844        {
5845            let message =
5846                format!("extraneous input {current_display} expecting {expected_display}");
5847            self.push_generated_parser_diagnostic(ParserDiagnostic {
5848                line: current_line,
5849                column: current_column,
5850                message,
5851                offending: Some(current),
5852            });
5853            self.record_syntax_errors(1);
5854            self.generated_sync_expected = None;
5855            // Single-token deletion: skip `current`, then accept `next`. The
5856            // accepted token can be EOF only if it is a real EOF terminal.
5857            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5858            self.consume();
5859            self.consume();
5860            self.reset_generated_recovery_state();
5861            return Ok(GeneratedMatch {
5862                children: GeneratedMatchChildren::Many(vec![
5863                    self.error_tree(current),
5864                    self.terminal_tree(next),
5865                ]),
5866                consumed_eof,
5867            });
5868        }
5869        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5870        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5871        // *after* the current element can consume the current symbol. At EOF that
5872        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5873        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5874        // when EOF merely leaks in from the empty enclosing context (as in
5875        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5876        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5877        // so consult the follow state's OWN expected set for the explicit case.
5878        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5879            && self
5880                .cached_state_expected_symbols(atn, follow_state)
5881                .contains(&TOKEN_EOF);
5882        if follow_symbols.contains(&current_type)
5883            && (current_type != TOKEN_EOF
5884                || self.rule_context_stack.len() > 1
5885                || expected_symbols.is_empty()
5886                || follow_explicitly_expects_eof)
5887        {
5888            let message = format!("missing {expected_display} at {current_display}");
5889            self.push_generated_parser_diagnostic(ParserDiagnostic {
5890                line: current_line,
5891                column: current_column,
5892                message,
5893                offending: Some(current),
5894            });
5895            self.record_syntax_errors(1);
5896            self.generated_sync_expected = None;
5897            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
5898            let missing_display = expected_symbol_display(token_type, self.vocabulary());
5899            let token = self.insert_synthetic_token(
5900                token_type,
5901                format!("<missing {missing_display}>"),
5902                current_line,
5903                current_column,
5904            )?;
5905            // Single-token insertion synthesizes a missing token and consumes
5906            // nothing, so no EOF terminal is consumed even when the lookahead is
5907            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5908            // from recording EOF as the rule stop on this recovery path.
5909            return Ok(GeneratedMatch {
5910                children: GeneratedMatchChildren::One(self.error_tree(token)),
5911                consumed_eof: false,
5912            });
5913        }
5914        let mismatch_expected_display = self
5915            .generated_sync_expected
5916            .take()
5917            .map_or(expected_display, |symbols| {
5918                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
5919            });
5920        Err(AntlrError::ParserError {
5921            line: current_line,
5922            column: current_column,
5923            message: format!(
5924                "mismatched input {current_display} expecting {mismatch_expected_display}"
5925            ),
5926            offending: Some(current),
5927        })
5928    }
5929
5930    fn generated_recovery_follow_symbols(
5931        &mut self,
5932        atn: &Atn,
5933        follow_state: usize,
5934    ) -> BTreeSet<i32> {
5935        let mut follow = self
5936            .cached_state_expected_symbols(atn, follow_state)
5937            .as_ref()
5938            .clone();
5939        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5940            follow.extend(self.context_expected_symbols(atn));
5941        }
5942        follow
5943    }
5944
5945    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5946        self.match_token(TOKEN_EOF)
5947    }
5948
5949    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5950        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5951    }
5952
5953    pub fn match_not_set(
5954        &mut self,
5955        intervals: &[(i32, i32)],
5956        min_vocabulary: i32,
5957        max_vocabulary: i32,
5958    ) -> Result<ParseTree, AntlrError> {
5959        self.match_interval_condition(intervals, |symbol| {
5960            (min_vocabulary..=max_vocabulary).contains(&symbol)
5961                && !interval_set_contains(intervals, symbol)
5962        })
5963    }
5964
5965    fn match_interval_condition(
5966        &mut self,
5967        intervals: &[(i32, i32)],
5968        matches: impl FnOnce(i32) -> bool,
5969    ) -> Result<ParseTree, AntlrError> {
5970        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5971            line: 0,
5972            column: 0,
5973            message: "missing current token".to_owned(),
5974            offending: None,
5975        })?;
5976        let current_type = self.token_type_for_id(current);
5977        if matches(current_type) {
5978            self.reset_generated_recovery_state();
5979            self.consume();
5980            Ok(self.terminal_tree(current))
5981        } else {
5982            Err(AntlrError::MismatchedInput {
5983                expected: self.interval_display(intervals),
5984                found: self.vocabulary().display_name(current_type),
5985            })
5986        }
5987    }
5988
5989    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5990        let values = intervals
5991            .iter()
5992            .map(|(start, stop)| {
5993                if start == stop {
5994                    self.vocabulary().display_name(*start)
5995                } else {
5996                    format!(
5997                        "{}..{}",
5998                        self.vocabulary().display_name(*start),
5999                        self.vocabulary().display_name(*stop)
6000                    )
6001                }
6002            })
6003            .collect::<Vec<_>>()
6004            .join(", ");
6005        format!("{{{values}}}")
6006    }
6007
6008    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
6009        if self.build_parse_trees {
6010            self.tree.finish_rule(context)
6011        } else {
6012            NodeId::placeholder()
6013        }
6014    }
6015
6016    /// Reports whether the generated rule dispatch should sample native stack
6017    /// capacity before descending into the next rule body.
6018    ///
6019    /// Generated recursive-descent methods otherwise map unbounded grammar
6020    /// nesting straight onto native call depth; sampling every
6021    /// [`GENERATED_RULE_STACK_CHECK_INTERVAL`] rule-context frames keeps the
6022    /// hot path free of per-call probes while guaranteeing a check runs before
6023    /// the red zone can be crossed.
6024    #[must_use]
6025    pub const fn generated_rule_stack_check_due(&self) -> bool {
6026        self.rule_context_stack
6027            .len()
6028            .is_multiple_of(GENERATED_RULE_STACK_CHECK_INTERVAL)
6029    }
6030
6031    /// Returns the positioned error to abort with when the configured
6032    /// rule-nesting depth cap would be exceeded by one more level, or `None`
6033    /// to keep parsing.
6034    ///
6035    /// Generated rule dispatch calls this before deepening — ahead of the
6036    /// rule-frame push at the dispatch boundary and ahead of each
6037    /// left-recursive expansion — letting callers parsing untrusted input
6038    /// bound CPU and tree memory ([`Parser::set_max_rule_depth`]). The
6039    /// inline fast path is one `Option` check when no cap is set (the
6040    /// default) and one addition plus compare when one is; only an actual
6041    /// violation leaves the inline path.
6042    ///
6043    /// The violation is sticky: rule-level recovery absorbs the returned
6044    /// error like any other rule failure and would otherwise keep spending
6045    /// the very resources the cap exists to bound, so every check after the
6046    /// first violation fails until [`Self::take_rule_depth_error`] drains it
6047    /// at the top-level entry.
6048    #[inline]
6049    pub fn rule_depth_cap_violation(&mut self) -> Option<AntlrError> {
6050        let max = self.max_rule_depth?;
6051        // Left-recursive operator iterations deepen the tree without pushing
6052        // a rule frame, so they count alongside the rule-context stack.
6053        if self.rule_depth_error.is_none()
6054            && self.rule_context_stack.len() + self.recursion_expansions < max
6055        {
6056            return None;
6057        }
6058        Some(self.rule_depth_cap_violation_cold(max))
6059    }
6060
6061    #[cold]
6062    fn rule_depth_cap_violation_cold(&mut self, max: usize) -> AntlrError {
6063        if let Some(error) = &self.rule_depth_error {
6064            return error.clone();
6065        }
6066        let current = self.input.lt(1);
6067        let (line, column) = current
6068            .as_ref()
6069            .map_or((0, 0), |token| (token.line(), token.column()));
6070        let error = AntlrError::ParserError {
6071            line,
6072            column,
6073            message: format!("rule nesting depth limit of {max} exceeded"),
6074            offending: current.as_ref().map(Token::token_id),
6075        };
6076        self.rule_depth_error = Some(error.clone());
6077        error
6078    }
6079
6080    /// Drains the sticky depth-cap violation recorded by
6081    /// [`Self::rule_depth_cap_violation`], if any.
6082    ///
6083    /// Generated top-level rule entries call this after recognition so a
6084    /// recovered parse that crossed the cap still fails, and so a reused
6085    /// parser starts its next parse clean.
6086    pub const fn take_rule_depth_error(&mut self) -> Option<AntlrError> {
6087        self.rule_depth_error.take()
6088    }
6089
6090    /// Reports whether a rule-nesting depth cap is configured.
6091    ///
6092    /// Generated dispatch consults this when selecting between the guarded
6093    /// recursive-descent body and the ATN-preferred interpreted fast path:
6094    /// only the generated body enforces the cap, so a configured bound
6095    /// overrides the performance preference.
6096    #[must_use]
6097    pub const fn has_rule_depth_cap(&self) -> bool {
6098        self.max_rule_depth.is_some()
6099    }
6100
6101    /// Registers a listener for committed rule enter/exit events during
6102    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
6103    /// the delivery contract.
6104    pub fn add_parse_listener<L>(&mut self, listener: L)
6105    where
6106        L: ParseListener + 'static,
6107    {
6108        self.parse_listeners
6109            .push(ParseListenerSlot(Box::new(listener)));
6110    }
6111
6112    /// Removes every registered parse listener and returns them, dropping any
6113    /// sticky abort a removed listener had requested.
6114    ///
6115    /// Returning the boxed listeners gives callers back the state they
6116    /// accumulated (depth counters, collected events) without threading
6117    /// shared handles through the listener.
6118    pub fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
6119        self.parse_listener_abort = None;
6120        self.parse_listeners.drain(..).map(|slot| slot.0).collect()
6121    }
6122
6123    /// Reports whether any parse listener is registered.
6124    ///
6125    /// Generated dispatch consults this alongside [`Self::has_rule_depth_cap`]
6126    /// when choosing between the generated body (which fires events) and the
6127    /// ATN-preferred interpreted fast path (which does not).
6128    #[must_use]
6129    pub const fn has_parse_listeners(&self) -> bool {
6130        !self.parse_listeners.is_empty()
6131    }
6132
6133    /// Reports whether semantic hooks may override interpreted decisions.
6134    ///
6135    /// Generated parsers use this to keep adaptive performance routing from
6136    /// changing parse semantics after a decision DFA becomes warm.
6137    #[doc(hidden)]
6138    #[must_use]
6139    pub fn observes_parser_decisions(&self) -> bool {
6140        self.semantic_hooks.observes_parser_decisions()
6141    }
6142
6143    /// Fires `enter_every_rule` on registered parse listeners, returning the
6144    /// abort error if any listener requested one.
6145    ///
6146    /// Generated rule dispatch calls this after the depth-cap probe and
6147    /// before the rule body runs; the generated left-recursive loop calls it
6148    /// once per operator expansion, mirroring upstream ANTLR's simulated
6149    /// rule-entry event for `pushNewRecursionContext`. A listener abort is
6150    /// sticky exactly like a depth-cap violation: rule-level recovery absorbs
6151    /// the returned error, so the flag holds until the top-level entry drains
6152    /// it via [`Self::take_parse_listener_abort`] and fails the parse.
6153    pub fn parse_listener_enter_rule(&mut self, rule_index: usize) -> Option<AntlrError> {
6154        if self.parse_listeners.is_empty() {
6155            return None;
6156        }
6157        self.parse_listener_enter_rule_dispatch(rule_index)
6158    }
6159
6160    fn parse_listener_enter_rule_dispatch(&mut self, rule_index: usize) -> Option<AntlrError> {
6161        if let Some(error) = &self.parse_listener_abort {
6162            return Some(error.clone());
6163        }
6164        let event = EnterRuleEvent {
6165            rule_index,
6166            current: self.input.lt(1),
6167        };
6168        // Split borrows: the token view borrows the input while listeners
6169        // need `&mut`, so listeners are taken out for the dispatch. Listener
6170        // methods have no parser access and cannot observe the absence.
6171        let mut listeners = std::mem::take(&mut self.parse_listeners);
6172        let mut abort = None;
6173        for slot in &mut listeners {
6174            if let Err(error) = slot.0.enter_every_rule(&event) {
6175                abort = Some(error);
6176                break;
6177            }
6178        }
6179        self.parse_listeners = listeners;
6180        if let Some(error) = abort {
6181            self.parse_listener_abort = Some(error.clone());
6182            return Some(error);
6183        }
6184        None
6185    }
6186
6187    /// Fires `exit_every_rule` on registered parse listeners.
6188    ///
6189    /// Generated rule bodies call this on every exit path — success and
6190    /// recovery alike — keeping enter/exit pairs balanced, and the generated
6191    /// left-recursive loop calls it once per operator expansion when the rule
6192    /// finishes unrolling.
6193    pub fn parse_listener_exit_rule(&mut self, rule_index: usize) {
6194        if self.parse_listeners.is_empty() {
6195            return;
6196        }
6197        // Reverse registration order, matching upstream ANTLR
6198        // (`Parser.triggerExitRuleEvent` walks listeners back to front).
6199        for slot in self.parse_listeners.iter_mut().rev() {
6200            slot.0.exit_every_rule(rule_index);
6201        }
6202    }
6203
6204    /// Drains the sticky parse-listener abort recorded by
6205    /// [`Self::parse_listener_enter_rule`], if any.
6206    ///
6207    /// Generated top-level rule entries call this after recognition so an
6208    /// aborted parse fails even when recovery produced a tree, and so a
6209    /// reused parser starts its next parse clean.
6210    pub const fn take_parse_listener_abort(&mut self) -> Option<AntlrError> {
6211        self.parse_listener_abort.take()
6212    }
6213
6214    /// Drains every sticky parse abort — the depth-cap violation and the
6215    /// parse-listener abort — returning the depth error preferentially.
6216    ///
6217    /// Generated top-level rule entries call this on both exit paths: the
6218    /// recorded abort wins over errors derived from it (recovery may have
6219    /// absorbed the aborted rule and failed differently later), a recovered
6220    /// `Ok` tree still fails when an abort was recorded, and draining leaves
6221    /// the instance clean for the next entry-rule call.
6222    pub fn take_parse_abort(&mut self) -> Option<AntlrError> {
6223        if let Some(error) = self.rule_depth_error.take() {
6224            self.parse_listener_abort = None;
6225            return Some(error);
6226        }
6227        self.parse_listener_abort.take()
6228    }
6229
6230    /// Enters a generated parser rule and returns the context object the
6231    /// generated method should populate.
6232    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
6233        self.set_state(state);
6234        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
6235        self.rule_context_stack.push(RuleContextFrame {
6236            rule_index,
6237            invoking_state,
6238        });
6239        self.advance_rule_context_version();
6240        let start_index = self.current_visible_index();
6241        let mut context = ParserRuleContext::new(rule_index, invoking_state);
6242        if let Some(token) = self.token_id_at(start_index) {
6243            self.set_context_start(&mut context, token);
6244        }
6245        context
6246    }
6247
6248    /// Records the ATN source state for the next generated rule invocation.
6249    ///
6250    /// ANTLR's full-context prediction reconstructs caller follow states from
6251    /// each active rule context's invoking state. Generated Rust rule methods are
6252    /// plain functions, so the caller supplies that ATN state just before making a
6253    /// rule call; `enter_rule` consumes it when the callee starts.
6254    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
6255        let marker = self.pending_invoking_states.len();
6256        self.pending_invoking_states.push(invoking_state);
6257        marker
6258    }
6259
6260    /// Discards an invoking-state marker if the callee did not consume it.
6261    pub fn discard_invoking_state(&mut self, marker: usize) {
6262        self.pending_invoking_states.truncate(marker);
6263    }
6264
6265    /// Exits the current generated parser rule.
6266    pub fn exit_rule(&mut self) {
6267        self.rule_context_stack.pop();
6268        self.advance_rule_context_version();
6269    }
6270
6271    /// Returns caller follow states for interning in a parser ATN simulator's
6272    /// prediction store. States are yielded outermost to innermost.
6273    pub fn prediction_context_return_states<'a>(
6274        &'a self,
6275        atn: &'a Atn,
6276    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
6277        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
6278            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
6279                return None;
6280            };
6281            let Some(Transition::Rule { follow_state, .. }) = atn
6282                .state(state_number)
6283                .and_then(|state| state.transitions().first())
6284                .map(ParserTransition::data)
6285            else {
6286                return None;
6287            };
6288            Some(follow_state)
6289        })
6290    }
6291
6292    /// Returns a generation that changes whenever the active rule stack changes.
6293    ///
6294    /// A parser ATN simulator uses this to reuse an interned outer prediction
6295    /// context while generated predictions remain in the same rule context.
6296    pub const fn rule_context_version(&self) -> usize {
6297        self.rule_context_version
6298    }
6299
6300    const fn advance_rule_context_version(&mut self) {
6301        self.rule_context_version = self.rule_context_version.wrapping_add(1);
6302    }
6303
6304    /// Adds a generated parser child only when parse-tree construction is
6305    /// enabled. The match is recorded on the context either way (via `add_child`,
6306    /// or `note_matched_child` when trees are off) so generated recovery can tell
6307    /// whether the rule has matched anything yet without depending on `children`.
6308    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
6309        if self.build_parse_trees {
6310            self.tree.add_child(context, child);
6311        } else {
6312            context.note_matched_child();
6313        }
6314    }
6315
6316    fn release_tree_scratch_if_idle(&mut self) {
6317        if self.rule_context_stack.is_empty() {
6318            self.tree.release_scratch();
6319        }
6320    }
6321
6322    /// Finishes a generated parser rule and returns its parse-tree node.
6323    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
6324        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6325        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6326            self.set_context_stop(&mut context, token);
6327        }
6328        let node = self.rule_node(context);
6329        self.exit_rule();
6330        self.release_tree_scratch_if_idle();
6331        node
6332    }
6333
6334    /// Recovers a generated rule catch block after a committed mismatch.
6335    ///
6336    /// ANTLR's generated parsers catch recognition errors inside each rule,
6337    /// report the original error, then consume unexpected tokens until the
6338    /// caller's recovery set can resume. Tokens consumed during recovery become
6339    /// error nodes in the current rule context.
6340    pub fn recover_generated_rule(
6341        &mut self,
6342        context: &mut ParserRuleContext,
6343        atn: &Atn,
6344        error: AntlrError,
6345    ) {
6346        let diagnostic = self.generated_rule_error_diagnostic(error);
6347        self.push_generated_parser_diagnostic(diagnostic);
6348        self.generated_sync_expected = None;
6349        let error_index = self.input.index();
6350        let error_state = self.data.state();
6351        // Match ANTLR's lastErrorIndex/lastErrorStates failsafe: a recovery
6352        // token can also be in the caller's follow set, leaving the cursor
6353        // unchanged and allowing generated outer decisions to revisit the same
6354        // failed state forever.
6355        if self.generated_recovery_error_index == Some(error_index)
6356            && self.generated_recovery_error_states.contains(&error_state)
6357            && self.la(1) != TOKEN_EOF
6358            && let Some(token) = self.input.lt_id(1)
6359        {
6360            self.consume();
6361            let child = self.error_tree(token);
6362            self.add_parse_child(context, child);
6363        }
6364        let recovery_index = self.input.index();
6365        if self.generated_recovery_error_index != Some(recovery_index) {
6366            self.generated_recovery_error_index = Some(recovery_index);
6367            self.generated_recovery_error_states.clear();
6368        }
6369        self.generated_recovery_error_states.insert(error_state);
6370        let recovery_symbols = self.context_expected_symbols(atn);
6371        loop {
6372            let symbol = self.la(1);
6373            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
6374                break;
6375            }
6376            let Some(token) = self.input.lt_id(1) else {
6377                break;
6378            };
6379            self.consume();
6380            let child = self.error_tree(token);
6381            self.add_parse_child(context, child);
6382        }
6383        self.record_syntax_errors(1);
6384    }
6385
6386    fn reset_generated_recovery_state(&mut self) {
6387        if self.generated_recovery_error_index.is_some() {
6388            self.generated_recovery_error_index = None;
6389            self.generated_recovery_error_states.clear();
6390        }
6391    }
6392
6393    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
6394        if self
6395            .generated_parser_diagnostics
6396            .iter()
6397            .any(|existing| existing == &diagnostic)
6398        {
6399            return;
6400        }
6401        self.generated_parser_diagnostics.push(diagnostic);
6402    }
6403
6404    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
6405        match error {
6406            // The anchor recorded where the error was built wins over the
6407            // current lookahead: prediction restores the cursor, so lt(1)
6408            // here can point at the decision start rather than the error.
6409            AntlrError::ParserError {
6410                line,
6411                column,
6412                message,
6413                offending,
6414            } => ParserDiagnostic {
6415                line,
6416                column,
6417                message,
6418                offending,
6419            },
6420            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
6421                self.input.lt(1),
6422                format!("mismatched input {found} expecting {expected}"),
6423            ),
6424            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
6425                self.input.lt(1),
6426                format!("no viable alternative at input {input}"),
6427            ),
6428            AntlrError::LexerError {
6429                line,
6430                column,
6431                message,
6432            } => ParserDiagnostic {
6433                line,
6434                column,
6435                message,
6436                offending: None,
6437            },
6438            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
6439        }
6440    }
6441
6442    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
6443    pub fn finish_recursion_rule(
6444        &mut self,
6445        mut context: ParserRuleContext,
6446        consumed_eof: bool,
6447    ) -> ParseTree {
6448        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6449        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6450            self.set_context_stop(&mut context, token);
6451        }
6452        let node = self.rule_node(context);
6453        self.unroll_recursion_context();
6454        self.release_tree_scratch_if_idle();
6455        node
6456    }
6457
6458    /// Enters a generated left-recursive rule at `precedence`.
6459    pub fn enter_recursion_rule(
6460        &mut self,
6461        state: isize,
6462        rule_index: usize,
6463        precedence: i32,
6464    ) -> ParserRuleContext {
6465        self.precedence_stack.push(precedence);
6466        self.recursion_expansion_marks
6467            .push(self.recursion_expansions);
6468        self.enter_rule(state, rule_index)
6469    }
6470
6471    /// Replaces the current context while expanding a left-recursive rule.
6472    pub fn push_new_recursion_context(
6473        &mut self,
6474        state: isize,
6475        rule_index: usize,
6476    ) -> ParserRuleContext {
6477        self.set_state(state);
6478        // Counts toward the depth cap: upstream treats this as rule entry
6479        // (`Parser.pushNewRecursionContext` fires `triggerEnterRuleEvent`).
6480        self.recursion_expansions += 1;
6481        ParserRuleContext::new(rule_index, state)
6482    }
6483
6484    /// Wraps the previous left-recursive context before parsing the next
6485    /// recursive operator alternative.
6486    pub fn push_new_recursion_context_with_previous(
6487        &mut self,
6488        state: isize,
6489        rule_index: usize,
6490        current: &mut ParserRuleContext,
6491    ) {
6492        self.set_state(state);
6493        // Counts toward the depth cap: each operator iteration deepens the
6494        // parse tree one level without pushing a rule frame, and upstream
6495        // fires a rule-entry listener event for it. The parse-listener enter
6496        // event for this expansion fires from the generated loop's probe
6497        // just before this call, where a listener abort can propagate.
6498        self.recursion_expansions += 1;
6499        if let Some(stop) = self
6500            .rule_stop_token_index(self.input.index(), false)
6501            .and_then(|index| self.token_id_at(index))
6502        {
6503            self.set_context_stop(current, stop);
6504        }
6505        let invoking_state = current.invoking_state();
6506        let start = current.start_id();
6507        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
6508        if start.is_some() {
6509            replacement.set_start_from_context(current);
6510        }
6511        let previous = std::mem::replace(current, replacement);
6512        if self.build_parse_trees {
6513            let previous = self.rule_node(previous);
6514            self.tree.add_child(current, previous);
6515        }
6516    }
6517
6518    /// Leaves a generated left-recursive rule.
6519    pub fn unroll_recursion_context(&mut self) {
6520        if self.precedence_stack.len() > 1 {
6521            self.precedence_stack.pop();
6522        }
6523        // Parse-listener exits for expansions fire inside the generated
6524        // operator loop (top of each pass, upstream's `recRuleSetPrevCtx`),
6525        // and the dispatch wrapper's exit covers the final live context —
6526        // upstream's `unrollRecursionContexts` walks exactly one link, so no
6527        // batched exits happen here. Only the depth-cap accounting rewinds.
6528        if let Some(mark) = self.recursion_expansion_marks.pop() {
6529            self.recursion_expansions = mark;
6530        }
6531        self.exit_rule();
6532    }
6533
6534    /// Predicts a generated left-recursive loop from one-token lookahead.
6535    ///
6536    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
6537    /// `None` means caller overlap, a dangerous multi-token prefix, or an
6538    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
6539    /// prediction (which includes the exit alt and precedence filtering).
6540    ///
6541    /// Single-token operators and multi-token prefixes that do not shadow a
6542    /// lower-precedence single-token operator keep the one-token enter fast path.
6543    ///
6544    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
6545    /// operator must not force enter; the adaptive decision may need to select
6546    /// the loop exit instead.
6547    pub fn left_recursive_loop_enter_prediction(
6548        &mut self,
6549        atn: &Atn,
6550        state_number: usize,
6551        precedence: i32,
6552    ) -> Option<bool> {
6553        let symbol = self.la(1);
6554        if symbol == TOKEN_EOF {
6555            return Some(false);
6556        }
6557        let operator_lookahead =
6558            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
6559        let can_single = operator_lookahead.single_token.contains(symbol);
6560        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
6561        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
6562        if !can_single && !can_multi && !can_predicate {
6563            return Some(false);
6564        }
6565        if can_predicate && !can_single {
6566            return None;
6567        }
6568        // Multi-token-only at this precedence, but the same symbol is a
6569        // single-token operator at precedence 0: defer so exit can win when the
6570        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
6571        if !can_single && can_multi && precedence > 0 {
6572            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
6573            if baseline.single_token.contains(symbol) {
6574                return None;
6575            }
6576        }
6577        let atn_key = SharedAtnCacheKey::for_atn(atn);
6578        let cached_overlap = self
6579            .left_recursive_caller_overlap_cache
6580            .iter()
6581            .flatten()
6582            .find(|entry| {
6583                entry.atn_key == atn_key
6584                    && entry.state_number == state_number
6585                    && entry.symbol == symbol
6586                    && entry.context_version == self.rule_context_version
6587            })
6588            .map(|entry| entry.overlaps);
6589        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
6590            let overlaps = caller_context_can_match_symbol_before_state(
6591                atn,
6592                self.prediction_context_return_states(atn),
6593                state_number,
6594                symbol,
6595            );
6596            if let Some(slot) = self
6597                .left_recursive_caller_overlap_cache
6598                .iter_mut()
6599                .find(|slot| slot.is_none())
6600            {
6601                *slot = Some(LeftRecursiveCallerOverlap {
6602                    atn_key,
6603                    state_number,
6604                    symbol,
6605                    context_version: self.rule_context_version,
6606                    overlaps,
6607                });
6608            }
6609            overlaps
6610        });
6611        if caller_overlaps {
6612            return None;
6613        }
6614        Some(true)
6615    }
6616
6617    fn cached_left_recursive_operator_lookahead(
6618        atn: &Atn,
6619        state_number: usize,
6620        precedence: i32,
6621    ) -> Rc<LeftRecursiveOperatorLookahead> {
6622        with_shared_atn_caches(atn, |cache| {
6623            let key = (state_number, precedence);
6624            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
6625                return Rc::clone(cached);
6626            }
6627            let lookahead = Rc::new(left_recursive_operator_lookahead(
6628                atn,
6629                state_number,
6630                precedence,
6631            ));
6632            cache
6633                .left_recursive_operator_lookahead
6634                .insert(key, Rc::clone(&lookahead));
6635            lookahead
6636        })
6637    }
6638
6639    /// Checks whether a generated left-recursive loop can unambiguously enter
6640    /// its operator alternative from one-token lookahead.
6641    pub fn left_recursive_loop_enter_matches(
6642        &mut self,
6643        atn: &Atn,
6644        state_number: usize,
6645        precedence: i32,
6646    ) -> bool {
6647        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
6648    }
6649
6650    /// Implements generated `precpred(_ctx, k)` checks.
6651    pub fn precpred(&self, precedence: i32) -> bool {
6652        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
6653    }
6654
6655    /// Evaluates a generated parser semantic predicate at the current input
6656    /// position.
6657    pub fn parser_semantic_predicate_matches(
6658        &mut self,
6659        predicates: &[(usize, usize, ParserPredicate)],
6660        rule_index: usize,
6661        pred_index: usize,
6662    ) -> bool {
6663        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
6664    }
6665
6666    /// Evaluates a generated parser semantic predicate with the current integer
6667    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
6668    pub fn parser_semantic_predicate_matches_with_local(
6669        &mut self,
6670        predicates: &[(usize, usize, ParserPredicate)],
6671        rule_index: usize,
6672        pred_index: usize,
6673        local_int_arg: i32,
6674    ) -> bool {
6675        self.parser_semantic_predicate_matches_inner(
6676            predicates,
6677            rule_index,
6678            pred_index,
6679            Some((rule_index, i64::from(local_int_arg))),
6680        )
6681    }
6682
6683    fn parser_semantic_predicate_matches_inner(
6684        &mut self,
6685        predicates: &[(usize, usize, ParserPredicate)],
6686        rule_index: usize,
6687        pred_index: usize,
6688        local_int_arg: Option<(usize, i64)>,
6689    ) -> bool {
6690        let index = self.input.index();
6691        let member_values = self.int_members.clone();
6692        self.parser_predicate_matches(PredicateEval {
6693            index,
6694            rule_index,
6695            pred_index,
6696            predicates,
6697            semantics: None,
6698            context: None,
6699            local_int_arg,
6700            member_values: &member_values,
6701        })
6702    }
6703
6704    /// Evaluates a generated parser semantic predicate with access to the
6705    /// current generated rule context.
6706    pub fn parser_semantic_predicate_matches_with_context_and_local(
6707        &mut self,
6708        predicates: &[(usize, usize, ParserPredicate)],
6709        rule_index: usize,
6710        pred_index: usize,
6711        context: &ParserRuleContext,
6712        local_int_arg: i32,
6713    ) -> bool {
6714        let index = self.input.index();
6715        let member_values = self.int_members.clone();
6716        self.parser_predicate_matches(PredicateEval {
6717            index,
6718            rule_index,
6719            pred_index,
6720            predicates,
6721            semantics: None,
6722            context: Some(context),
6723            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6724            member_values: &member_values,
6725        })
6726    }
6727
6728    /// Evaluates a generated `SemIR` parser predicate with access to the current
6729    /// generated rule context.
6730    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
6731        &mut self,
6732        semantics: &ParserSemantics,
6733        rule_index: usize,
6734        pred_index: usize,
6735        context: &ParserRuleContext,
6736        local_int_arg: i32,
6737    ) -> bool {
6738        let index = self.input.index();
6739        let member_values = self.int_members.clone();
6740        self.parser_predicate_matches(PredicateEval {
6741            index,
6742            rule_index,
6743            pred_index,
6744            predicates: &[],
6745            semantics: Some(semantics),
6746            context: Some(context),
6747            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6748            member_values: &member_values,
6749        })
6750    }
6751
6752    /// Returns a generated fail-option message for a parser semantic
6753    /// predicate coordinate.
6754    pub fn parser_semantic_predicate_failure_message(
6755        &self,
6756        rule_index: usize,
6757        pred_index: usize,
6758        predicates: &[(usize, usize, ParserPredicate)],
6759    ) -> Option<&'static str> {
6760        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
6761    }
6762
6763    /// Matches any non-EOF token.
6764    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
6765        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6766            line: 0,
6767            column: 0,
6768            message: "missing current token".to_owned(),
6769            offending: None,
6770        })?;
6771        if self.token_type_for_id(current) == TOKEN_EOF {
6772            return Err(AntlrError::MismatchedInput {
6773                expected: "wildcard".to_owned(),
6774                found: self.vocabulary().display_name(TOKEN_EOF),
6775            });
6776        }
6777        self.reset_generated_recovery_state();
6778        self.consume();
6779        Ok(self.terminal_tree(current))
6780    }
6781
6782    /// Generated parser synchronization hook. The current interpreter owns
6783    /// recovery; direct generated methods can call this as a no-op until the
6784    /// generated recovery strategy is expanded.
6785    #[allow(clippy::unnecessary_wraps)]
6786    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
6787        self.set_state(state);
6788        Ok(())
6789    }
6790
6791    /// Synchronizes a generated parser decision against the ATN lookahead set.
6792    ///
6793    /// ANTLR generated parsers call the error strategy before optional and loop
6794    /// decisions. When the current token cannot start any alternative, follow a
6795    /// nullable exit, or be deleted before a later synchronization token, the
6796    /// generated Rust method reports that decision-level mismatch instead of
6797    /// descending into a child rule that cannot start at the current token.
6798    pub fn sync_decision(
6799        &mut self,
6800        atn: &Atn,
6801        state_number: usize,
6802        current_context_empty: bool,
6803        loop_back: bool,
6804    ) -> Result<Vec<ParseTree>, AntlrError> {
6805        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
6806        self.generated_sync_expected = None;
6807        let Some(state) = atn.state(state_number) else {
6808            return Ok(Vec::new());
6809        };
6810        let Some(rule_index) = state.rule_index() else {
6811            return Ok(Vec::new());
6812        };
6813        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
6814            return Ok(Vec::new());
6815        };
6816        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6817        let symbol = self.la(1);
6818        let mut has_expected_symbols = false;
6819        let mut nullable = false;
6820        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
6821        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
6822        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
6823        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
6824        // and worth deleting; an implicit-follow EOF means the loop should simply
6825        // exit and leave the token for the (absent) caller — matching ANTLR, which
6826        // exits the loop via prediction rather than consuming up to a synthetic EOF.
6827        let mut explicit_eof_expected = false;
6828        for transition in &entry.transitions {
6829            if transition.symbols.contains(symbol) {
6830                return Ok(Vec::new());
6831            }
6832            has_expected_symbols |= !transition.symbols.is_empty();
6833            nullable |= transition.nullable;
6834            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
6835        }
6836        // Happy path: a nullable decision exits when the symbol is in the
6837        // rule-stack follow set. Answer the membership question with an
6838        // early-exit walk; the full union below is only needed for the
6839        // mismatch/deletion diagnostics.
6840        if nullable && self.context_expected_contains(atn, symbol) {
6841            return Ok(Vec::new());
6842        }
6843        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
6844        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
6845            return Ok(Vec::new());
6846        }
6847        let mut expected = TokenBitSet::default();
6848        for transition in &entry.transitions {
6849            expected.extend_from(&transition.symbols);
6850        }
6851        if let Some(context_expected) = context_expected {
6852            expected.extend_from(&context_expected);
6853        }
6854        let can_delete_in_place =
6855            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
6856        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
6857        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
6858        // least one iteration) does `consumeUntil` the follow set — multi-token
6859        // deletion, one error per skipped token across iterations; a loop ENTRY
6860        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
6861        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
6862        // unexpected token only when LA(2) is expected, otherwise report a mismatch
6863        // and leave recovery to the rule.
6864        //
6865        // The generated loop always presents the loop-ENTRY state to this method on
6866        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
6867        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
6868        // an iteration has been taken, and true on a `+` loop's first sync since its
6869        // mandatory first element is iteration 1). Treating a loop entry as a
6870        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
6871        // `c`s, which ANTLR rejects with `mismatched input`).
6872        let loop_sync = loop_back;
6873        if symbol != TOKEN_EOF && can_delete_in_place {
6874            let mut cursor = self.input.index();
6875            let mut skipped = Vec::new();
6876            loop {
6877                let current = self.token_type_at(cursor);
6878                if current == TOKEN_EOF {
6879                    break;
6880                }
6881                skipped.push(cursor);
6882                let next = self.consume_index(cursor, current);
6883                if next == cursor {
6884                    break;
6885                }
6886                let next_symbol = self.token_type_at(next);
6887                // Stop (and delete the skipped tokens as error nodes) when the next
6888                // token is a real expected continuation. EOF counts only when it is
6889                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
6890                // genuinely extraneous and the generated EOF match consumes the real
6891                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
6892                // rule-follow) does NOT count — the loop must exit and leave the
6893                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
6894                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
6895                    explicit_eof_expected
6896                } else {
6897                    expected.contains(next_symbol)
6898                };
6899                if next_is_expected_stop {
6900                    let current_token = self.input.lt(1);
6901                    let expected_symbols = expected.to_btree_set();
6902                    let message = format!(
6903                        "extraneous input {} expecting {}",
6904                        current_token
6905                            .as_ref()
6906                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6907                        self.expected_symbols_display(&expected_symbols)
6908                    );
6909                    self.push_generated_parser_diagnostic(diagnostic_for_token(
6910                        current_token,
6911                        message,
6912                    ));
6913                    self.record_syntax_errors(1);
6914                    let mut children = Vec::with_capacity(skipped.len());
6915                    for index in skipped {
6916                        if let Some(token) = self.token_id_at(index) {
6917                            self.consume();
6918                            children.push(self.error_tree(token));
6919                        }
6920                    }
6921                    if !loop_sync {
6922                        self.reset_generated_recovery_state();
6923                    }
6924                    return Ok(children);
6925                }
6926                // A non-loop block entry deletes at most one token (single-token
6927                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6928                // is reported at the first token instead of skipping ahead.
6929                if !loop_sync {
6930                    break;
6931                }
6932                cursor = next;
6933            }
6934        }
6935        if nullable {
6936            self.generated_sync_expected = Some(expected);
6937            return Ok(Vec::new());
6938        }
6939        let current = self.input.lt(1);
6940        let expected_symbols = expected.to_btree_set();
6941        Err(AntlrError::ParserError {
6942            line: current.as_ref().map(Token::line).unwrap_or_default(),
6943            column: current.as_ref().map(Token::column).unwrap_or_default(),
6944            message: format!(
6945                "mismatched input {} expecting {}",
6946                current
6947                    .as_ref()
6948                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6949                self.expected_symbols_display(&expected_symbols)
6950            ),
6951            offending: current.as_ref().map(Token::token_id),
6952        })
6953    }
6954
6955    /// Returns a generated-parser prediction when one token of lookahead
6956    /// uniquely selects an alternative for `state_number`.
6957    ///
6958    /// This mirrors the interpreter's LL(1) commit point and lets generated
6959    /// recursive-descent methods avoid invoking the adaptive simulator for
6960    /// simple optional/block/loop decisions.
6961    pub fn ll1_decision_prediction(
6962        &mut self,
6963        atn: &Atn,
6964        state_number: usize,
6965    ) -> Option<ParserAtnPrediction> {
6966        let state = atn.state(state_number)?;
6967        if state.precedence_rule_decision() {
6968            return None;
6969        }
6970        let rule_stop = state
6971            .rule_index()
6972            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6973        let symbol = self.la(1);
6974        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6975        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6976            alt: alt + 1,
6977            requires_full_context: false,
6978            has_semantic_context: false,
6979            diagnostic: None,
6980        })
6981    }
6982
6983    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6984        let mut expected = BTreeSet::new();
6985        for index in (1..self.rule_context_stack.len()).rev() {
6986            let invoking_state = self.rule_context_stack[index].invoking_state;
6987            let Ok(state_number) = usize::try_from(invoking_state) else {
6988                continue;
6989            };
6990            let Some(Transition::Rule { follow_state, .. }) = atn
6991                .state(state_number)
6992                .and_then(|state| state.transitions().first())
6993                .map(ParserTransition::data)
6994            else {
6995                continue;
6996            };
6997            let return_state = follow_state;
6998            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6999            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
7000                return expected;
7001            }
7002        }
7003        expected.insert(TOKEN_EOF);
7004        expected
7005    }
7006
7007    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
7008        let mut expected = TokenBitSet::default();
7009        for index in (1..self.rule_context_stack.len()).rev() {
7010            let invoking_state = self.rule_context_stack[index].invoking_state;
7011            let Ok(state_number) = usize::try_from(invoking_state) else {
7012                continue;
7013            };
7014            let Some(Transition::Rule { follow_state, .. }) = atn
7015                .state(state_number)
7016                .and_then(|state| state.transitions().first())
7017                .map(ParserTransition::data)
7018            else {
7019                continue;
7020            };
7021            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
7022            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
7023                return expected;
7024            }
7025        }
7026        expected.insert(TOKEN_EOF);
7027        expected
7028    }
7029
7030    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
7031    /// without materializing the union.
7032    ///
7033    /// This walks the rule-invocation stack directly, innermost frame first —
7034    /// the same frames, in the same order, with the same rule-stop gating as
7035    /// the same outer-context return-state chain used by adaptive prediction.
7036    /// The nullable
7037    /// exit in `sync_decision` asks only this membership question, and on
7038    /// valid input the innermost frame answers it, so the early exit replaces
7039    /// an O(stack-depth) set union per loop/optional exit with one probe.
7040    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
7041        for index in (1..self.rule_context_stack.len()).rev() {
7042            let invoking_state = self.rule_context_stack[index].invoking_state;
7043            let Ok(state_number) = usize::try_from(invoking_state) else {
7044                continue;
7045            };
7046            let Some(Transition::Rule { follow_state, .. }) = atn
7047                .state(state_number)
7048                .and_then(|state| state.transitions().first())
7049                .map(ParserTransition::data)
7050            else {
7051                continue;
7052            };
7053            if self
7054                .cached_state_expected_token_set(atn, follow_state)
7055                .contains(symbol)
7056            {
7057                return true;
7058            }
7059            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
7060                return false;
7061            }
7062        }
7063        symbol == TOKEN_EOF
7064    }
7065
7066    /// Builds a generated no-viable-alternative parser error.
7067    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
7068        let error_index = self.input.index();
7069        self.no_viable_alternative_error_at(start_index, error_index)
7070    }
7071
7072    /// Builds a generated no-viable-alternative parser error at the simulator's
7073    /// failing lookahead index. `adaptive_predict` restores the input cursor
7074    /// before returning, so generated parsers have to pass the recorded index
7075    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
7076    pub fn no_viable_alternative_error_at(
7077        &self,
7078        start_index: usize,
7079        error_index: usize,
7080    ) -> AntlrError {
7081        let diagnostic = self.no_viable_alternative(start_index, error_index);
7082        AntlrError::ParserError {
7083            line: diagnostic.line,
7084            column: diagnostic.column,
7085            message: diagnostic.message,
7086            offending: diagnostic.offending,
7087        }
7088    }
7089
7090    /// Builds a generated failed-predicate parser error.
7091    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
7092        let current = self.input.lt(1);
7093        AntlrError::ParserError {
7094            line: current.as_ref().map(Token::line).unwrap_or_default(),
7095            column: current.as_ref().map(Token::column).unwrap_or_default(),
7096            message: format!("rule failed predicate: {}", message.into()),
7097            offending: current.as_ref().map(Token::token_id),
7098        }
7099    }
7100
7101    /// Builds a generated parser error for a semantic predicate with ANTLR's
7102    /// `<fail='...'>` option.
7103    pub fn failed_predicate_option_error(
7104        &self,
7105        rule_index: usize,
7106        message: impl Into<String>,
7107    ) -> AntlrError {
7108        let current = self.input.lt(1);
7109        let rule_name = self
7110            .rule_names()
7111            .get(rule_index)
7112            .map_or_else(|| rule_index.to_string(), Clone::clone);
7113        AntlrError::ParserError {
7114            line: current.as_ref().map(Token::line).unwrap_or_default(),
7115            column: current.as_ref().map(Token::column).unwrap_or_default(),
7116            message: format!("rule {rule_name} {}", message.into()),
7117            offending: current.as_ref().map(Token::token_id),
7118        }
7119    }
7120
7121    /// Builds a generated parser-action event at the current input position.
7122    pub fn parser_action_at_current(
7123        &mut self,
7124        source_state: usize,
7125        rule_index: usize,
7126        start_index: usize,
7127        consumed_eof: bool,
7128    ) -> ParserAction {
7129        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
7130        ParserAction::new(source_state, rule_index, start_index, stop_index)
7131    }
7132
7133    /// Offers a committed parser action event to the user semantic hook.
7134    ///
7135    /// Generated parsers call this for action source states that were present
7136    /// in the ATN but not translated into a built-in Rust action template.
7137    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
7138        let rule_index = action.rule_index();
7139        let rule_name = self.rule_names().get(rule_index).cloned();
7140        let context = None;
7141        let input = &mut self.input;
7142        let semantic_hooks = &mut self.semantic_hooks;
7143        let member_values = &self.int_members;
7144        let mut ctx = ParserSemCtx {
7145            input,
7146            tree_storage: &self.tree,
7147            rule_index,
7148            coordinate_index: usize::MAX,
7149            rule_name,
7150            context,
7151            tree: Some(tree),
7152            local_int_arg: None,
7153            member_values,
7154            action: Some(action),
7155        };
7156        let handled = semantic_hooks.action(&mut ctx, action);
7157        // This action reached the hook because it had no translated arm. If no
7158        // hook handled it either (`SemanticHooks::action` returns `false`), the
7159        // committed action is silently dropped — record it so the parse entry
7160        // can fail loud under the fail-loud boundary, mirroring unknown
7161        // predicates. `assume-*` policies opt out of the fail-loud recording.
7162        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
7163            let coordinate = (rule_index, action.source_state());
7164            if !self.unhandled_action_hits.contains(&coordinate) {
7165                self.unhandled_action_hits.push(coordinate);
7166            }
7167        }
7168        handled
7169    }
7170
7171    /// Attempts to execute a whole generated rule by committing simulator
7172    /// decisions directly. Unsupported constructs or decisions that need
7173    /// full-context / predicate evaluation restore the input cursor and fall
7174    /// back to [`Self::parse_atn_rule`].
7175    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
7176        &mut self,
7177        atn: &'atn Atn,
7178        simulator: &mut ParserAtnSimulator<'atn>,
7179        rule_index: usize,
7180    ) -> Result<ParseTree, AntlrError> {
7181        let start_index = self.current_visible_index();
7182        self.clear_prediction_diagnostics();
7183        self.reset_per_parse_caches();
7184        self.reset_recognition_arena();
7185        let tree_checkpoint = self.tree.checkpoint();
7186        let mut decision_by_state = vec![None; atn.states().len()];
7187        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
7188            if let Some(slot) = decision_by_state.get_mut(state_number) {
7189                *slot = Some(decision);
7190            }
7191        }
7192
7193        let result = DirectAdaptiveParser {
7194            parser: self,
7195            atn,
7196            simulator,
7197            decision_by_state,
7198            steps: 0,
7199        }
7200        .parse_rule(rule_index, -1, 0);
7201
7202        match result {
7203            Ok(tree) => {
7204                self.report_token_source_errors();
7205                self.release_tree_scratch_if_idle();
7206                Ok(tree)
7207            }
7208            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
7209                let _ = reason;
7210                self.tree.rollback(tree_checkpoint);
7211                self.input.seek(start_index);
7212                self.parse_atn_rule(atn, rule_index)
7213            }
7214        }
7215    }
7216
7217    /// Parses a generated rule by interpreting the parser ATN from the rule's
7218    /// start state to its stop state.
7219    ///
7220    /// The recognizer backtracks across alternatives and loop exits using token
7221    /// stream indices instead of committing to input consumption immediately.
7222    /// Once a viable ATN path is found, the parser commits the accepted token
7223    /// interval and returns a rule node whose children mirror every grammar
7224    /// rule invocation reached on that path, matching ANTLR's parse-tree
7225    /// shape.
7226    pub fn parse_atn_rule(
7227        &mut self,
7228        atn: &Atn,
7229        rule_index: usize,
7230    ) -> Result<ParseTree, AntlrError> {
7231        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
7232    }
7233
7234    /// Parses a generated rule by interpreting the parser ATN with an initial
7235    /// left-recursive precedence threshold.
7236    pub fn parse_atn_rule_with_precedence(
7237        &mut self,
7238        atn: &Atn,
7239        rule_index: usize,
7240        precedence: i32,
7241    ) -> Result<ParseTree, AntlrError> {
7242        self.parse_atn_rule_with_precedence_inner(
7243            atn,
7244            rule_index,
7245            precedence,
7246            None,
7247            AltNumberTracking::default(),
7248        )
7249    }
7250
7251    fn parse_atn_rule_with_precedence_inner(
7252        &mut self,
7253        atn: &Atn,
7254        rule_index: usize,
7255        precedence: i32,
7256        predicate_context: Option<FastPredicateContext<'_>>,
7257        alt_tracking: AltNumberTracking,
7258    ) -> Result<ParseTree, AntlrError> {
7259        let report_unrecovered_error = self.is_top_level_entry();
7260        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7261            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7262        })?;
7263        let stop_state = atn
7264            .rule_to_stop_state()
7265            .get(rule_index)
7266            .filter(|state| *state != usize::MAX)
7267            .ok_or_else(|| {
7268                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7269            })?;
7270
7271        let start_index = self.current_visible_index();
7272        self.clear_prediction_diagnostics();
7273        self.reset_per_parse_caches();
7274        self.reset_recognition_arena();
7275        let caller_follow_state = self.pending_invoking_follow_state(atn);
7276        self.fast_recovery_enabled = false;
7277        self.fast_token_nodes_enabled = false;
7278        self.fast_track_alt_numbers = alt_tracking.any();
7279        let top_request = FastRecognizeTopRequest {
7280            start_state,
7281            stop_state,
7282            start_index,
7283            precedence,
7284            caller_follow_state,
7285        };
7286        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
7287        self.fast_token_nodes_enabled = self.build_parse_trees;
7288        let needs_tree_retry = matches!(
7289            &first_pass,
7290            Ok((outcome, _, _))
7291                if self.build_parse_trees
7292                    && self
7293                        .recognition_arena
7294                        .sequence_has_left_recursive_boundary(outcome.nodes)
7295        );
7296        let needs_retry = match &first_pass {
7297            // The FIRST-set prefilter trims speculative rule calls that can't
7298            // match the current lookahead — useful for perf on grammars with
7299            // many epsilon-reachable rules, but the trim also bypasses
7300            // single-token insertion / deletion recovery that ANTLR's
7301            // reference parser runs at the child rule's first consuming
7302            // transition. Retry without the prefilter whenever the first pass
7303            // either produced no outcome at all or produced a recovered
7304            // outcome (diagnostics non-empty), since the second pass might
7305            // surface a child-level recovery with cleaner diagnostics or
7306            // closer parity to ANTLR's tree shape. Left-recursive tree
7307            // boundaries also need the token-node pass; otherwise the fold has
7308            // no concrete left operand to wrap into ANTLR's recursive context.
7309            Err(_) => true,
7310            Ok((outcome, _, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
7311        };
7312        let (outcome, _expected, alt_number) = if needs_retry {
7313            self.fast_first_set_prefilter = false;
7314            self.fast_recovery_enabled = false;
7315            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7316            let clean_selected = if needs_tree_retry {
7317                match clean_retry {
7318                    ok @ Ok(_) => ok,
7319                    Err(_) => first_pass,
7320                }
7321            } else {
7322                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
7323            };
7324            let selected = if clean_selected.is_err()
7325                || matches!(&clean_selected, Ok((outcome, _, _)) if !outcome.diagnostics.is_empty())
7326            {
7327                self.fast_recovery_enabled = true;
7328                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7329                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
7330            } else {
7331                clean_selected
7332            };
7333            self.fast_first_set_prefilter = true;
7334            self.fast_recovery_enabled = true;
7335            selected.map_err(|expected| {
7336                if predicate_context.is_some()
7337                    && let Some(error) = self.unknown_semantic_error()
7338                {
7339                    self.report_token_source_errors();
7340                    return error;
7341                }
7342                let error = self.recognition_error(rule_index, start_index, &expected);
7343                self.record_syntax_errors(1);
7344                self.report_token_source_errors();
7345                if report_unrecovered_error {
7346                    self.report_unrecovered_parser_error(&error);
7347                }
7348                error
7349            })?
7350        } else {
7351            first_pass.expect("first_pass is Ok in the no-retry branch")
7352        };
7353        if predicate_context.is_some()
7354            && let Some(error) = self.unknown_semantic_error()
7355        {
7356            self.report_token_source_errors();
7357            return Err(error);
7358        }
7359        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7360        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7361        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7362        self.report_token_source_errors();
7363        let mut context = ParserRuleContext::with_child_capacity(
7364            rule_index,
7365            self.state(),
7366            if self.build_parse_trees {
7367                self.recognition_arena.sequence_len(outcome.nodes)
7368            } else {
7369                0
7370            },
7371        );
7372        if alt_tracking.public {
7373            context.set_alt_number(alt_number.max(1));
7374        }
7375        if alt_tracking.context {
7376            context.set_context_alt_number(alt_number);
7377        }
7378        if let Some(token) = self.token_id_at(start_index) {
7379            self.set_context_start(&mut context, token);
7380        }
7381        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
7382        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
7383            self.set_context_stop(&mut context, token);
7384        }
7385        let live_root = if self.build_parse_trees {
7386            self.recognition_arena
7387                .fold_left_recursive_boundaries(outcome.nodes)
7388        } else {
7389            outcome.nodes
7390        };
7391        if self.build_parse_trees {
7392            if self
7393                .recognition_arena
7394                .sequence_has_explicit_token(live_root)
7395            {
7396                let mut cursor = live_root;
7397                while let Some(link) = self.recognition_arena.link(cursor) {
7398                    let child = self.arena_recognized_node_tree(
7399                        link.head,
7400                        alt_tracking.public,
7401                        alt_tracking.context,
7402                    )?;
7403                    self.tree.add_child(&mut context, child);
7404                    cursor = link.tail;
7405                }
7406            } else {
7407                self.add_arena_implicit_token_children(
7408                    &mut context,
7409                    start_index,
7410                    stop_index,
7411                    live_root,
7412                    alt_tracking,
7413                )?;
7414            }
7415        }
7416        self.finish_recognition_arena(live_root, outcome.diagnostics);
7417        self.input.seek(outcome.index);
7418
7419        let tree = self.rule_node(context);
7420        self.release_tree_scratch_if_idle();
7421        Ok(tree)
7422    }
7423
7424    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
7425        let invoking_state = self.pending_invoking_states.last().copied()?;
7426        let state_number = usize::try_from(invoking_state).ok()?;
7427        match atn.state(state_number)?.transitions().first()?.data() {
7428            Transition::Rule { follow_state, .. } => Some(follow_state),
7429            _ => None,
7430        }
7431    }
7432
7433    #[cfg(test)]
7434    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
7435        caller_follow_token_info_for_stream(&mut self.input, index)
7436    }
7437
7438    /// Runs the fast recognizer once from the rule's start state and returns
7439    /// the best outcome or the per-attempt expected-token accumulator. The
7440    /// caller flips `fast_first_set_prefilter` between calls when a retry is
7441    /// needed, so the FIRST-set cache is left intact across both passes.
7442    fn fast_recognize_top(
7443        &mut self,
7444        atn: &Atn,
7445        request: FastRecognizeTopRequest,
7446        predicate_context: Option<FastPredicateContext<'_>>,
7447    ) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
7448        let FastRecognizeTopRequest {
7449            start_state,
7450            stop_state,
7451            start_index,
7452            precedence,
7453            caller_follow_state,
7454        } = request;
7455        // `input.size()` is intentionally only the currently buffered token
7456        // count here. Do not restore an up-front fill just to size this map:
7457        // a small floor avoids tiny-input churn, and larger inputs reserve from
7458        // the buffered token count without forcing startup tokenization. The
7459        // 8x multiplier matches the empirical
7460        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
7461        // Kotlin ~12× memo entries per token), so the table avoids one
7462        // rehash on the typical hot path.
7463        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
7464        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
7465        recognize_scratch.prepare(memo_capacity);
7466        let mut expected = ExpectedTokens::default();
7467        let empty_recovery = self.empty_recovery_symbols();
7468        let outcomes = self.recognize_state_fast(
7469            atn,
7470            FastRecognizeRequest {
7471                state_number: start_state,
7472                stop_state,
7473                index: start_index,
7474                rule_start_index: start_index,
7475                decision_start_index: None,
7476                precedence,
7477                depth: 0,
7478                recovery_symbols: empty_recovery,
7479                recovery_state: None,
7480            },
7481            FastRecognizeScratch {
7482                predicate_context,
7483                visiting: &mut recognize_scratch.visiting,
7484                memo: &mut recognize_scratch.memo,
7485                expected: &mut expected,
7486                native_depth: 0,
7487            },
7488        );
7489        recognize_scratch.release_oversized_memo();
7490        self.fast_recognize_scratch = recognize_scratch;
7491        #[cfg(feature = "perf-counters")]
7492        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
7493            perf_counters::dump();
7494            perf_counters::reset();
7495        }
7496        let caller_follow =
7497            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
7498        let selected = {
7499            let arena = &self.recognition_arena;
7500            let input = &mut self.input;
7501            select_best_fast_outcome(
7502                outcomes.into_iter(),
7503                self.prediction_mode,
7504                caller_follow.as_deref(),
7505                |index| caller_follow_token_info_for_stream(input, index),
7506                arena,
7507            )
7508        };
7509        match selected {
7510            Some(mut outcome) => {
7511                let alt_number = if self.build_parse_trees || self.fast_track_alt_numbers {
7512                    self.materialize_fast_outcome_nodes(&mut outcome)
7513                } else {
7514                    0
7515                };
7516                Ok((outcome, expected, alt_number))
7517            }
7518            None => Err(expected),
7519        }
7520    }
7521
7522    /// Converts one speculative arena record into the flat public CST.
7523    fn arena_recognized_node_tree(
7524        &mut self,
7525        node_id: RecognizedNodeId,
7526        track_alt_numbers: bool,
7527        track_context_alt_numbers: bool,
7528    ) -> Result<ParseTree, AntlrError> {
7529        let node = self.recognition_arena.node(node_id);
7530        match node {
7531            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
7532            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
7533            ArenaRecognizedNode::MissingToken { extra } => {
7534                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
7535                    RecognitionExtra::MissingToken {
7536                        token_type,
7537                        at_index,
7538                        text,
7539                    } => (*token_type, *at_index as usize, text.clone()),
7540                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
7541                        unreachable!("missing-token node must reference missing-token extra")
7542                    }
7543                };
7544                let (line, column) = self
7545                    .token_at(at_index)
7546                    .map_or((0, 0), |token| (token.line(), token.column()));
7547                let token = self.insert_synthetic_token(token_type, text, line, column)?;
7548                Ok(self.error_tree(token))
7549            }
7550            ArenaRecognizedNode::Rule {
7551                rule_index,
7552                invoking_state,
7553                alt_number,
7554                start_index,
7555                stop_index,
7556                return_values,
7557                children,
7558            } => {
7559                let mut context = ParserRuleContext::with_child_capacity(
7560                    rule_index as usize,
7561                    invoking_state as isize,
7562                    self.recognition_arena.sequence_len(children),
7563                );
7564                if track_alt_numbers {
7565                    context.set_alt_number((alt_number as usize).max(1));
7566                }
7567                if track_context_alt_numbers {
7568                    context.set_context_alt_number(alt_number as usize);
7569                }
7570                if let Some(extra) = return_values {
7571                    let RecognitionExtra::ReturnValues(values) =
7572                        self.recognition_arena.extra(extra)
7573                    else {
7574                        unreachable!("rule node must reference return-values extra");
7575                    };
7576                    for (name, value) in values {
7577                        context.set_int_return(name.clone(), *value);
7578                    }
7579                }
7580                if let Some(token) = self.token_id_at(start_index as usize) {
7581                    self.set_context_start(&mut context, token);
7582                }
7583                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7584                    self.set_context_stop(&mut context, token);
7585                }
7586                let mut cursor = self
7587                    .recognition_arena
7588                    .fold_left_recursive_boundaries(children);
7589                while let Some(link) = self.recognition_arena.link(cursor) {
7590                    let child = self.arena_recognized_node_tree(
7591                        link.head,
7592                        track_alt_numbers,
7593                        track_context_alt_numbers,
7594                    )?;
7595                    self.tree.add_child(&mut context, child);
7596                    cursor = link.tail;
7597                }
7598                Ok(self.rule_node(context))
7599            }
7600            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index, .. } => {
7601                Err(AntlrError::Unsupported(format!(
7602                    "unfolded left-recursive boundary for rule {rule_index}"
7603                )))
7604            }
7605        }
7606    }
7607
7608    fn arena_recognized_node_tree_with_implicit_tokens(
7609        &mut self,
7610        node_id: RecognizedNodeId,
7611        alt_tracking: AltNumberTracking,
7612    ) -> Result<ParseTree, AntlrError> {
7613        let node = self.recognition_arena.node(node_id);
7614        match node {
7615            ArenaRecognizedNode::Rule {
7616                rule_index,
7617                invoking_state,
7618                alt_number,
7619                start_index,
7620                stop_index,
7621                children,
7622                ..
7623            } => {
7624                let mut context = ParserRuleContext::with_child_capacity(
7625                    rule_index as usize,
7626                    invoking_state as isize,
7627                    self.recognition_arena.sequence_len(children),
7628                );
7629                if alt_tracking.public {
7630                    context.set_alt_number((alt_number as usize).max(1));
7631                }
7632                if alt_tracking.context {
7633                    context.set_context_alt_number(alt_number as usize);
7634                }
7635                if let Some(token) = self.token_id_at(start_index as usize) {
7636                    self.set_context_start(&mut context, token);
7637                }
7638                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7639                    self.set_context_stop(&mut context, token);
7640                }
7641                let children = self
7642                    .recognition_arena
7643                    .fold_left_recursive_boundaries(children);
7644                self.add_arena_implicit_token_children(
7645                    &mut context,
7646                    start_index as usize,
7647                    stop_index.map(|index| index as usize),
7648                    children,
7649                    alt_tracking,
7650                )?;
7651                Ok(self.rule_node(context))
7652            }
7653            _ => {
7654                self.arena_recognized_node_tree(node_id, alt_tracking.public, alt_tracking.context)
7655            }
7656        }
7657    }
7658
7659    fn add_arena_implicit_token_children(
7660        &mut self,
7661        context: &mut ParserRuleContext,
7662        start_index: usize,
7663        stop_index: Option<usize>,
7664        mut children: NodeSeqId,
7665        alt_tracking: AltNumberTracking,
7666    ) -> Result<(), AntlrError> {
7667        let mut cursor = Some(start_index);
7668        while let Some(link) = self.recognition_arena.link(children) {
7669            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
7670                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
7671                let child =
7672                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7673                self.tree.add_child(context, child);
7674                if let Some(child_stop) = child_stop {
7675                    let next = self.next_visible_after_token(child_stop);
7676                    cursor = match (cursor, next) {
7677                        (None, _) | (_, None) => None,
7678                        (Some(current), Some(next)) => Some(current.max(next)),
7679                    };
7680                }
7681            } else {
7682                let child =
7683                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7684                self.tree.add_child(context, child);
7685            }
7686            children = link.tail;
7687        }
7688        if let Some(stop) = stop_index {
7689            self.add_visible_terminals_through(context, cursor, stop)?;
7690        }
7691        Ok(())
7692    }
7693
7694    fn add_visible_terminals_before(
7695        &mut self,
7696        context: &mut ParserRuleContext,
7697        cursor: &mut Option<usize>,
7698        before: usize,
7699    ) -> Result<(), AntlrError> {
7700        let Some(stop) = before.checked_sub(1) else {
7701            return Ok(());
7702        };
7703        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
7704        *cursor = next;
7705        Ok(())
7706    }
7707
7708    fn add_visible_terminals_through(
7709        &mut self,
7710        context: &mut ParserRuleContext,
7711        mut cursor: Option<usize>,
7712        stop: usize,
7713    ) -> Result<Option<usize>, AntlrError> {
7714        while let Some(index) = cursor {
7715            if index > stop {
7716                return Ok(Some(index));
7717            }
7718            let token = self
7719                .input
7720                .get_id(index)
7721                .ok_or_else(|| AntlrError::ParserError {
7722                    line: 0,
7723                    column: 0,
7724                    message: format!("missing token at index {index}"),
7725                    offending: None,
7726                })?;
7727            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
7728            let child = self.terminal_tree(token);
7729            self.tree.add_child(context, child);
7730            if is_eof {
7731                return Ok(None);
7732            }
7733            cursor = self.next_visible_after_token(index);
7734        }
7735        Ok(None)
7736    }
7737
7738    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
7739        let next = self.input.next_visible_after(index);
7740        (next != index).then_some(next)
7741    }
7742
7743    /// Parses a generated rule and returns semantic actions reached on the
7744    /// selected ATN path.
7745    ///
7746    /// This slower path preserves action ordering and token intervals for
7747    /// generated code that replays target-specific action templates after the
7748    /// recognizer has chosen one viable parse path.
7749    pub fn parse_atn_rule_with_actions(
7750        &mut self,
7751        atn: &Atn,
7752        rule_index: usize,
7753    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7754        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
7755    }
7756
7757    /// Parses a generated rule and emits ATN actions plus selected rule-init
7758    /// actions reached on the chosen path.
7759    ///
7760    /// Generated parsers use this when a grammar contains rule-level `@init`
7761    /// templates that must run for nested rule invocations. The runtime keeps
7762    /// the action list path-sensitive, so init templates are replayed only for
7763    /// rules that were actually entered by the selected parse.
7764    pub fn parse_atn_rule_with_action_inits(
7765        &mut self,
7766        atn: &Atn,
7767        rule_index: usize,
7768        init_action_rules: &[usize],
7769    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7770        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7771    }
7772
7773    /// Parses a generated rule with optional semantic-action replay features.
7774    ///
7775    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7776    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7777    /// unchanged for grammars that do not request that target template.
7778    pub fn parse_atn_rule_with_action_options(
7779        &mut self,
7780        atn: &Atn,
7781        rule_index: usize,
7782        init_action_rules: &[usize],
7783        track_alt_numbers: bool,
7784    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7785        self.parse_atn_rule_with_runtime_options(
7786            atn,
7787            rule_index,
7788            ParserRuntimeOptions {
7789                init_action_rules,
7790                track_alt_numbers,
7791                ..ParserRuntimeOptions::default()
7792            },
7793        )
7794    }
7795
7796    /// Parses a generated rule with action replay and parser predicate support.
7797    ///
7798    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7799    /// target-template predicate semantics emitted by the generator. Missing
7800    /// entries are treated as true so unsupported predicate-free grammars keep
7801    /// the previous unconditional transition behavior.
7802    pub fn parse_atn_rule_with_runtime_options(
7803        &mut self,
7804        atn: &Atn,
7805        rule_index: usize,
7806        options: ParserRuntimeOptions<'_>,
7807    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7808        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7809    }
7810
7811    /// Parses a generated rule with action replay, parser predicate support,
7812    /// and an initial left-recursive precedence threshold.
7813    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7814        &mut self,
7815        atn: &Atn,
7816        rule_index: usize,
7817        precedence: i32,
7818        options: ParserRuntimeOptions<'_>,
7819    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7820        let report_unrecovered_error = self.is_top_level_entry();
7821        let ParserRuntimeOptions {
7822            init_action_rules,
7823            track_alt_numbers,
7824            track_context_alt_numbers,
7825            predicates,
7826            semantics,
7827            rule_args,
7828            member_actions,
7829            return_actions,
7830            unknown_predicate_policy,
7831        } = options;
7832        let capture_alt_numbers = track_alt_numbers || track_context_alt_numbers;
7833        if init_action_rules.is_empty()
7834            && !capture_alt_numbers
7835            && predicates.is_empty()
7836            && semantics.is_none()
7837            && rule_args.is_empty()
7838            && member_actions.is_empty()
7839            && return_actions.is_empty()
7840            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7841            && !atn_has_observable_action_transitions(atn)
7842            && !self.semantic_hooks.observes_parser_decisions()
7843            && (!self.semantic_hooks.observes_parser_predicates()
7844                || !atn_has_predicate_transitions(atn))
7845        {
7846            return self
7847                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7848                .map(|tree| (tree, Vec::new()));
7849        }
7850        if !self.semantic_hooks.observes_parser_decisions()
7851            && can_use_fast_predicate_recognizer(atn, &options)
7852        {
7853            self.unknown_predicate_policy = unknown_predicate_policy;
7854            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7855            let member_values = self.int_members.clone();
7856            let result = self
7857                .parse_atn_rule_with_precedence_inner(
7858                    atn,
7859                    rule_index,
7860                    precedence,
7861                    Some(FastPredicateContext {
7862                        predicates,
7863                        semantics,
7864                        member_values: &member_values,
7865                    }),
7866                    AltNumberTracking {
7867                        public: track_alt_numbers,
7868                        context: track_context_alt_numbers,
7869                    },
7870                )
7871                .map(|tree| (tree, Vec::new()));
7872            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7873                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7874            }
7875            return result;
7876        }
7877        self.unknown_predicate_policy = unknown_predicate_policy;
7878        // A generated parent may have already recorded unknown-predicate
7879        // coordinates before descending into this (interpreted) child. Clearing
7880        // unconditionally would drop them before the parent's public entry
7881        // surfaces them, so stash and restore around this call: recognition sees
7882        // only the hits it records itself (so the fail-loud check below reflects
7883        // this rule), and the parent's prior hits are merged back afterward.
7884        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7885        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7886            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7887        })?;
7888        let stop_state = atn
7889            .rule_to_stop_state()
7890            .get(rule_index)
7891            .filter(|state| *state != usize::MAX)
7892            .ok_or_else(|| {
7893                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7894            })?;
7895
7896        let start_index = self.current_visible_index();
7897        self.clear_prediction_diagnostics();
7898        self.reset_per_parse_caches();
7899        self.reset_recognition_arena();
7900        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7901        let invoking_state = self.pending_invoking_states.pop();
7902        let local_int_arg = invoking_state
7903            .and_then(|state| usize::try_from(state).ok())
7904            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7905        let mut visiting = BTreeSet::new();
7906        let mut memo = BTreeMap::new();
7907        let mut expected = ExpectedTokens::default();
7908        let member_values = self.int_members.clone();
7909        let return_values = BTreeMap::new();
7910        let outcomes = self.recognize_state(
7911            atn,
7912            RecognizeRequest {
7913                state_number: start_state,
7914                stop_state,
7915                index: start_index,
7916                rule_start_index: start_index,
7917                decision_start_index: None,
7918                init_action_rules: &init_action_rules,
7919                predicates,
7920                semantics,
7921                rule_args,
7922                member_actions,
7923                return_actions,
7924                local_int_arg,
7925                member_values,
7926                return_values,
7927                rule_alt_number: 0,
7928                track_alt_numbers: capture_alt_numbers,
7929                consumed_eof: false,
7930                committed_decision: false,
7931                precedence,
7932                depth: 0,
7933                recovery_symbols: BTreeSet::new(),
7934                recovery_state: None,
7935            },
7936            &mut visiting,
7937            &mut memo,
7938            &mut expected,
7939        );
7940        if let Some(error) = self.unknown_semantic_error() {
7941            self.report_token_source_errors();
7942            // Keep the recorded coordinates: when this interpreted rule is a
7943            // child of a generated parent, the parent's catch block recovers an
7944            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7945            // coordinate must survive on the parser for the top-level entry's
7946            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7947            // is handled by clearing at the top-level generated entry instead.
7948            return Err(error);
7949        }
7950        // Recognition recorded no unresolved coordinate of its own; merge the
7951        // parent's prior hits back so its public entry can still surface them.
7952        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7953        let Some(outcome) = select_best_outcome(
7954            outcomes.into_iter(),
7955            self.prediction_mode,
7956            &self.recognition_arena,
7957        ) else {
7958            let error = self.recognition_error(rule_index, start_index, &expected);
7959            self.record_syntax_errors(1);
7960            self.report_token_source_errors();
7961            if report_unrecovered_error {
7962                self.report_unrecovered_parser_error(&error);
7963            }
7964            return Err(error);
7965        };
7966
7967        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7968        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7969        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7970        self.report_token_source_errors();
7971        let mut actions = outcome.actions;
7972        if init_action_rules.contains(&rule_index) {
7973            actions.insert(
7974                0,
7975                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7976            );
7977        }
7978        let mut context =
7979            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7980        if track_alt_numbers {
7981            context.set_alt_number(outcome.alt_number.max(1));
7982        }
7983        if track_context_alt_numbers {
7984            context.set_context_alt_number(outcome.alt_number);
7985        }
7986        for (name, value) in outcome.return_values {
7987            context.set_int_return(name, value);
7988        }
7989        if let Some(token) = self.token_id_at(start_index) {
7990            self.set_context_start(&mut context, token);
7991        }
7992        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7993            self.set_context_stop(&mut context, token);
7994        }
7995        let live_root = if self.build_parse_trees {
7996            self.recognition_arena
7997                .fold_left_recursive_boundaries(outcome.nodes)
7998        } else {
7999            outcome.nodes
8000        };
8001        if self.build_parse_trees {
8002            let mut nodes = live_root;
8003            while let Some(link) = self.recognition_arena.link(nodes) {
8004                let child = self.arena_recognized_node_tree(
8005                    link.head,
8006                    track_alt_numbers,
8007                    track_context_alt_numbers,
8008                )?;
8009                self.tree.add_child(&mut context, child);
8010                nodes = link.tail;
8011            }
8012        }
8013        self.finish_recognition_arena(live_root, outcome.diagnostics);
8014        self.input.seek(outcome.index);
8015
8016        let tree = self.rule_node(context);
8017        self.release_tree_scratch_if_idle();
8018        Ok((tree, actions))
8019    }
8020
8021    /// Temporary parser entry used by generated parser methods while the parser
8022    /// ATN simulator is being implemented.
8023    ///
8024    /// This keeps generated parser crates buildable and gives us a stable method
8025    /// surface for every grammar rule. It intentionally accepts all remaining
8026    /// tokens into one rule context; it is not the final parser semantics.
8027    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
8028        let mut context = ParserRuleContext::new(rule_index, self.state());
8029        while self.la(1) != TOKEN_EOF {
8030            let token_type = self.la(1);
8031            let child = self.match_token(token_type)?;
8032            if self.build_parse_trees {
8033                self.tree.add_child(&mut context, child);
8034            }
8035        }
8036        if self.build_parse_trees {
8037            let child = self.match_eof()?;
8038            self.tree.add_child(&mut context, child);
8039        }
8040        let tree = self.rule_node(context);
8041        self.release_tree_scratch_if_idle();
8042        Ok(tree)
8043    }
8044
8045    /// Builds the parser error reported when no ATN path can reach the active
8046    /// rule stop state.
8047    fn recognition_error(
8048        &mut self,
8049        rule_index: usize,
8050        start_index: usize,
8051        expected: &ExpectedTokens,
8052    ) -> AntlrError {
8053        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
8054        self.input.seek(index);
8055        let current = self.input.lt(1);
8056        let line = current.as_ref().map(Token::line).unwrap_or_default();
8057        let column = current.as_ref().map(Token::column).unwrap_or_default();
8058        AntlrError::ParserError {
8059            line,
8060            column,
8061            message,
8062            offending: current.as_ref().map(Token::token_id),
8063        }
8064    }
8065
8066    /// Builds the token index and ANTLR-compatible message for a failed rule.
8067    fn expected_error_message(
8068        &mut self,
8069        rule_index: usize,
8070        start_index: usize,
8071        expected: &ExpectedTokens,
8072    ) -> (usize, String) {
8073        let index = expected
8074            .index
8075            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
8076            .unwrap_or_else(|| self.input.index());
8077        self.input.seek(index);
8078        let current = self.input.lt(1);
8079        let message = if expected
8080            .no_viable
8081            .as_ref()
8082            .is_some_and(|no_viable| no_viable.error_index == index)
8083        {
8084            let start = expected
8085                .no_viable
8086                .as_ref()
8087                .map_or(start_index, |no_viable| no_viable.start_index);
8088            let text = display_input_text(&self.input.text(start, index));
8089            format!("no viable alternative at input '{text}'")
8090        } else if expected.symbols.is_empty() {
8091            if expected.index.is_some() {
8092                let found = current
8093                    .as_ref()
8094                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
8095                if current
8096                    .as_ref()
8097                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
8098                {
8099                    format!(
8100                        "missing {} at {found}",
8101                        self.expected_symbols_display(&expected.symbols)
8102                    )
8103                } else {
8104                    format!("mismatched input {found}")
8105                }
8106            } else {
8107                format!("no viable alternative while parsing rule {rule_index}")
8108            }
8109        } else {
8110            format!(
8111                "mismatched input {} expecting {}",
8112                current
8113                    .as_ref()
8114                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8115                self.expected_symbols_display(&expected.symbols)
8116            )
8117        };
8118        (index, message)
8119    }
8120
8121    /// Converts a failed child rule into a recovered outcome so the parent can
8122    /// continue after reporting the child diagnostic.
8123    fn child_rule_failure_recovery(
8124        &mut self,
8125        rule_index: usize,
8126        start_index: usize,
8127        sync_symbols: &BTreeSet<i32>,
8128        member_values: MemberEnv,
8129        expected: &ExpectedTokens,
8130    ) -> Option<RecognizeOutcome> {
8131        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
8132        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
8133        let mut next_index = error_index;
8134        loop {
8135            let symbol = self.token_type_at(next_index);
8136            if sync_symbols.contains(&symbol) {
8137                if next_index == error_index {
8138                    return None;
8139                }
8140                break;
8141            }
8142            if symbol == TOKEN_EOF {
8143                break;
8144            }
8145            let after = self.consume_index(next_index, symbol);
8146            if after == next_index {
8147                break;
8148            }
8149            next_index = after;
8150        }
8151        let mut nodes = NodeSeqId::EMPTY;
8152        let error = self.arena_token_node(error_index, true);
8153        self.arena_prepend(&mut nodes, error);
8154        let diagnostics = self
8155            .recognition_arena
8156            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8157        Some(RecognizeOutcome {
8158            index: next_index,
8159            consumed_eof: false,
8160            alt_number: 0,
8161            member_values,
8162            return_values: BTreeMap::new(),
8163            diagnostics,
8164            decisions: Vec::new(),
8165            actions: Vec::new(),
8166            nodes,
8167        })
8168    }
8169
8170    /// Adapts the optional recovery result to the normal outcome list used by
8171    /// rule-call transitions.
8172    fn child_rule_failure_recovery_outcomes(
8173        &mut self,
8174        request: ChildRuleFailureRecovery<'_>,
8175    ) -> Vec<RecognizeOutcome> {
8176        let sync_symbols =
8177            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
8178        self.child_rule_failure_recovery(
8179            request.rule_index,
8180            request.start_index,
8181            &sync_symbols,
8182            request.member_values,
8183            request.expected,
8184        )
8185        .into_iter()
8186        .collect()
8187    }
8188
8189    /// Formats expected token types using ANTLR's single-token or set syntax.
8190    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
8191        expected_symbols_display(symbols, self.vocabulary())
8192    }
8193
8194    /// Returns the single-token deletion repair if the token after `index`
8195    /// satisfies the failed consuming transition.
8196    fn single_token_deletion(
8197        &mut self,
8198        transition: ParserTransition<'_>,
8199        index: usize,
8200        max_token_type: i32,
8201        expected_symbols: &BTreeSet<i32>,
8202    ) -> Option<(ParserDiagnostic, usize, i32)> {
8203        let current_symbol = self.token_type_at(index);
8204        if current_symbol == TOKEN_EOF {
8205            return None;
8206        }
8207        let next_index = self.consume_index(index, current_symbol);
8208        if next_index == index {
8209            return None;
8210        }
8211        let next_symbol = self.token_type_at(next_index);
8212        if !transition.matches(next_symbol, 1, max_token_type) {
8213            return None;
8214        }
8215        let transition_expected = transition_expected_symbols(transition, max_token_type);
8216        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8217            &transition_expected
8218        } else {
8219            expected_symbols
8220        });
8221        let current = self.token_at(index);
8222        let message = format!(
8223            "extraneous input {} expecting {expected_display}",
8224            current
8225                .as_ref()
8226                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8227        );
8228        Some((
8229            diagnostic_for_token(current, message),
8230            next_index,
8231            next_symbol,
8232        ))
8233    }
8234
8235    /// Returns the repair used when deleting the current token lets a recovery
8236    /// state continue with the following token.
8237    fn current_token_deletion(
8238        &mut self,
8239        index: usize,
8240        expected_symbols: &BTreeSet<i32>,
8241    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
8242        if expected_symbols.is_empty() {
8243            return None;
8244        }
8245        let current_symbol = self.token_type_at(index);
8246        if current_symbol == TOKEN_EOF {
8247            return None;
8248        }
8249        let current = self.token_at(index);
8250        let message = format!(
8251            "extraneous input {} expecting {}",
8252            current
8253                .as_ref()
8254                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8255            self.expected_symbols_display(expected_symbols)
8256        );
8257        let diagnostic = diagnostic_for_token(current, message);
8258        let mut skipped = Vec::new();
8259        let mut cursor = index;
8260        loop {
8261            let symbol = self.token_type_at(cursor);
8262            if symbol == TOKEN_EOF {
8263                return None;
8264            }
8265            skipped.push(cursor);
8266            let next_index = self.consume_index(cursor, symbol);
8267            if next_index == cursor {
8268                return None;
8269            }
8270            let next_symbol = self.token_type_at(next_index);
8271            if expected_symbols.contains(&next_symbol) {
8272                return Some((diagnostic, next_index, skipped));
8273            }
8274            cursor = next_index;
8275        }
8276    }
8277
8278    /// Returns the single-token insertion repair for a failed consuming
8279    /// transition. The caller validates the repair by continuing from the
8280    /// transition target at the same input index.
8281    fn single_token_insertion(
8282        &mut self,
8283        transition: ParserTransition<'_>,
8284        index: usize,
8285        max_token_type: i32,
8286        expected_symbols: &BTreeSet<i32>,
8287        follow_symbols: &BTreeSet<i32>,
8288    ) -> Option<(ParserDiagnostic, i32, String)> {
8289        let current_symbol = self.token_type_at(index);
8290        if !follow_symbols.contains(&current_symbol) {
8291            return None;
8292        }
8293        let transition_expected = transition_expected_symbols(transition, max_token_type);
8294        let token_type = transition_expected.iter().next().copied()?;
8295        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8296            &transition_expected
8297        } else {
8298            expected_symbols
8299        });
8300        let mut token_symbols = BTreeSet::new();
8301        token_symbols.insert(token_type);
8302        let missing_token_display = self.expected_symbols_display(&token_symbols);
8303        let current = self.token_at(index);
8304        let message = format!(
8305            "missing {expected_display} at {}",
8306            current
8307                .as_ref()
8308                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8309        );
8310        let text = format!("<missing {missing_token_display}>");
8311        Some((
8312            diagnostic_for_token(current.as_ref(), message),
8313            token_type,
8314            text,
8315        ))
8316    }
8317
8318    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
8319    /// skip the unexpected current token when the following token satisfies the
8320    /// transition that failed.
8321    fn fast_single_token_deletion_recovery(
8322        &mut self,
8323        recovery: FastRecoveryRequest<'_, '_>,
8324        predicate_context: Option<FastPredicateContext<'_>>,
8325    ) -> Vec<FastRecognizeOutcome> {
8326        let FastRecoveryRequest {
8327            atn,
8328            transition,
8329            expected_symbols,
8330            target,
8331            request,
8332            visiting,
8333            memo,
8334            expected,
8335        } = recovery;
8336        let FastRecognizeRequest {
8337            stop_state,
8338            index,
8339            rule_start_index,
8340            decision_start_index,
8341            precedence,
8342            depth,
8343            ..
8344        } = request;
8345        let Some((diagnostic, next_index, next_symbol)) =
8346            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8347        else {
8348            return Vec::new();
8349        };
8350        let after_next = self.consume_index(next_index, next_symbol);
8351        let empty_recovery = self.empty_recovery_symbols();
8352        self.recognize_state_fast(
8353            atn,
8354            FastRecognizeRequest {
8355                state_number: target,
8356                stop_state,
8357                index: after_next,
8358                rule_start_index,
8359                decision_start_index,
8360                precedence,
8361                depth: depth + 1,
8362                recovery_symbols: empty_recovery,
8363                recovery_state: None,
8364            },
8365            FastRecognizeScratch {
8366                predicate_context,
8367                visiting,
8368                memo,
8369                expected,
8370                native_depth: 0,
8371            },
8372        )
8373        .into_iter()
8374        .map(|mut outcome| {
8375            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
8376            outcome.diagnostics = self
8377                .recognition_arena
8378                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8379            if self.fast_token_nodes_enabled {
8380                let token = self.arena_token_node(next_index, false);
8381                self.defer_fast_outcome_node(&mut outcome, token);
8382                let error = self.arena_token_node(index, true);
8383                self.defer_fast_outcome_node(&mut outcome, error);
8384            }
8385            outcome
8386        })
8387        .collect()
8388    }
8389
8390    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
8391    /// pretend the expected transition token was present and continue without
8392    /// consuming the current token.
8393    fn fast_single_token_insertion_recovery(
8394        &mut self,
8395        recovery: FastRecoveryRequest<'_, '_>,
8396        predicate_context: Option<FastPredicateContext<'_>>,
8397    ) -> Vec<FastRecognizeOutcome> {
8398        let FastRecoveryRequest {
8399            atn,
8400            transition,
8401            expected_symbols,
8402            target,
8403            request,
8404            visiting,
8405            memo,
8406            expected,
8407        } = recovery;
8408        let FastRecognizeRequest {
8409            stop_state,
8410            index,
8411            rule_start_index,
8412            decision_start_index,
8413            precedence,
8414            depth,
8415            ..
8416        } = request;
8417        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
8418        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
8419            transition,
8420            index,
8421            atn.max_token_type(),
8422            &expected_symbols,
8423            &follow_symbols,
8424        ) else {
8425            return Vec::new();
8426        };
8427        let empty_recovery = self.empty_recovery_symbols();
8428        self.recognize_state_fast(
8429            atn,
8430            FastRecognizeRequest {
8431                state_number: target,
8432                stop_state,
8433                index,
8434                rule_start_index,
8435                decision_start_index,
8436                precedence,
8437                depth: depth + 1,
8438                recovery_symbols: empty_recovery,
8439                recovery_state: None,
8440            },
8441            FastRecognizeScratch {
8442                predicate_context,
8443                visiting,
8444                memo,
8445                expected,
8446                native_depth: 0,
8447            },
8448        )
8449        .into_iter()
8450        .map(|mut outcome| {
8451            outcome.diagnostics = self
8452                .recognition_arena
8453                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8454            let missing = self.arena_missing_token_node(token_type, index, text.clone());
8455            self.defer_fast_outcome_node(&mut outcome, missing);
8456            outcome
8457        })
8458        .collect()
8459    }
8460
8461    /// Retries the current fast-recognition state after deleting one
8462    /// unexpected token that precedes a valid loop or block continuation.
8463    fn fast_current_token_deletion_recovery(
8464        &mut self,
8465        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
8466        predicate_context: Option<FastPredicateContext<'_>>,
8467    ) -> Vec<FastRecognizeOutcome> {
8468        let FastCurrentTokenDeletionRequest {
8469            atn,
8470            expected_symbols,
8471            mut request,
8472            visiting,
8473            memo,
8474            expected,
8475        } = recovery;
8476        if request.index == request.rule_start_index {
8477            return Vec::new();
8478        }
8479        let Some((diagnostic, next_index, skipped)) =
8480            self.current_token_deletion(request.index, &expected_symbols)
8481        else {
8482            return Vec::new();
8483        };
8484        request.state_number = request.recovery_state.unwrap_or(request.state_number);
8485        request.index = next_index;
8486        request.depth += 1;
8487        request.recovery_state = None;
8488        self.recognize_state_fast(
8489            atn,
8490            request,
8491            FastRecognizeScratch {
8492                predicate_context,
8493                visiting,
8494                memo,
8495                expected,
8496                native_depth: 0,
8497            },
8498        )
8499        .into_iter()
8500        .map(|mut outcome| {
8501            outcome.diagnostics = self
8502                .recognition_arena
8503                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8504            for index in skipped.iter().rev() {
8505                let error = self.arena_token_node(*index, true);
8506                self.defer_fast_outcome_node(&mut outcome, error);
8507            }
8508            outcome
8509        })
8510        .collect()
8511    }
8512
8513    /// Converts a failed child rule into a recovered fast-recognizer outcome so
8514    /// the parent can keep its child rule context and continue at a sync token.
8515    fn fast_child_rule_failure_recovery(
8516        &mut self,
8517        rule_index: usize,
8518        start_index: usize,
8519        sync_symbols: &BTreeSet<i32>,
8520        expected: &ExpectedTokens,
8521    ) -> Option<FastRecognizeOutcome> {
8522        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
8523        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
8524        let mut next_index = error_index;
8525        loop {
8526            let symbol = self.token_type_at(next_index);
8527            if sync_symbols.contains(&symbol) {
8528                if next_index == error_index {
8529                    return None;
8530                }
8531                break;
8532            }
8533            if symbol == TOKEN_EOF {
8534                break;
8535            }
8536            let after = self.consume_index(next_index, symbol);
8537            if after == next_index {
8538                break;
8539            }
8540            next_index = after;
8541        }
8542        let diagnostics = self
8543            .recognition_arena
8544            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8545        let mut nodes = NodeSeqId::EMPTY;
8546        if self.fast_token_nodes_enabled {
8547            let error = self.arena_token_node(error_index, true);
8548            self.arena_prepend(&mut nodes, error);
8549        }
8550        Some(FastRecognizeOutcome {
8551            index: next_index,
8552            consumed_eof: false,
8553            diagnostics,
8554            deferred_nodes: FastDeferredNodeId::EMPTY,
8555            nodes,
8556        })
8557    }
8558
8559    /// Adapts the optional child-rule recovery result to the fast-recognizer
8560    /// outcome list used by rule-call transitions.
8561    fn fast_child_rule_failure_recovery_outcomes(
8562        &mut self,
8563        request: FastChildRuleFailureRecoveryRequest<'_>,
8564    ) -> Vec<FastRecognizeOutcome> {
8565        let FastChildRuleFailureRecoveryRequest {
8566            atn,
8567            rule_index,
8568            start_index,
8569            follow_state,
8570            stop_state,
8571            expected,
8572        } = request;
8573        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
8574        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
8575            .into_iter()
8576            .collect()
8577    }
8578
8579    fn defer_fast_outcome_node(
8580        &mut self,
8581        outcome: &mut FastRecognizeOutcome,
8582        node: RecognizedNodeId,
8583    ) {
8584        if outcome.deferred_nodes.is_empty() {
8585            self.arena_prepend(&mut outcome.nodes, node);
8586            return;
8587        }
8588        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
8589        let fragment = self.recognition_arena.deferred_fragment(fragment);
8590        outcome.deferred_nodes = self
8591            .recognition_arena
8592            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
8593    }
8594
8595    fn defer_fast_outcome_alternative(
8596        &mut self,
8597        outcome: &mut FastRecognizeOutcome,
8598        alt_number: usize,
8599    ) {
8600        let alternative = self.recognition_arena.deferred_alternative(alt_number);
8601        outcome.deferred_nodes = self
8602            .recognition_arena
8603            .concat_deferred_nodes(alternative, outcome.deferred_nodes);
8604    }
8605
8606    fn defer_fast_outcome_boundary(
8607        &mut self,
8608        outcome: &mut FastRecognizeOutcome,
8609        rule_index: usize,
8610    ) {
8611        let boundary = self
8612            .recognition_arena
8613            .deferred_left_recursive_boundary(rule_index);
8614        outcome.deferred_nodes = self
8615            .recognition_arena
8616            .concat_deferred_nodes(boundary, outcome.deferred_nodes);
8617    }
8618
8619    fn materialize_fast_deferred_nodes(
8620        &mut self,
8621        root: FastDeferredNodeId,
8622        initial_suffix: NodeSeqId,
8623    ) -> (NodeSeqId, usize) {
8624        if root.is_empty() {
8625            return (initial_suffix, 0);
8626        }
8627
8628        enum Frame {
8629            Visit(FastDeferredNodeId),
8630            ContinuePrefix(FastDeferredNodeId),
8631            FinishRule {
8632                rule: FastDeferredRule,
8633                parent_suffix: NodeSeqId,
8634                parent_alt_number: u32,
8635                parent_pending_boundary: Option<RecognizedNodeId>,
8636            },
8637        }
8638
8639        let mut result = initial_suffix;
8640        // The rope is visited suffix-first while nodes are prepended. Later
8641        // alternatives arrive first, so earlier markers overwrite them; a
8642        // boundary redirects those earlier markers to the wrapped context.
8643        let mut alt_number = 0;
8644        let mut pending_boundary = None;
8645        let mut pending = Vec::with_capacity(16);
8646        pending.push(Frame::Visit(root));
8647        let mut fragment_nodes = Vec::new();
8648        while let Some(frame) = pending.pop() {
8649            match frame {
8650                Frame::Visit(deferred) => {
8651                    if deferred.is_empty() {
8652                        continue;
8653                    }
8654
8655                    match self.recognition_arena.deferred_node(deferred) {
8656                        FastDeferredNode::Fragment(sequence) => {
8657                            fragment_nodes.clear();
8658                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
8659                            while let Some(node) = fragment_nodes.pop() {
8660                                self.arena_prepend(&mut result, node);
8661                            }
8662                        }
8663                        FastDeferredNode::Rule(rule) => {
8664                            let rule = self.recognition_arena.deferred_rule(rule);
8665                            let parent_suffix = result;
8666                            let parent_alt_number = alt_number;
8667                            let parent_pending_boundary = pending_boundary;
8668                            result = rule.children;
8669                            alt_number = 0;
8670                            pending_boundary = None;
8671                            pending.push(Frame::FinishRule {
8672                                rule,
8673                                parent_suffix,
8674                                parent_alt_number,
8675                                parent_pending_boundary,
8676                            });
8677                            pending.push(Frame::Visit(rule.deferred_children));
8678                        }
8679                        FastDeferredNode::Alternative(selected) => {
8680                            if let Some(boundary) = pending_boundary {
8681                                self.recognition_arena
8682                                    .set_boundary_alt_number(boundary, selected);
8683                            } else {
8684                                alt_number = selected;
8685                            }
8686                        }
8687                        FastDeferredNode::LeftRecursiveBoundary { rule_index } => {
8688                            let boundary = self.arena_boundary_node(rule_index as usize, 0);
8689                            self.arena_prepend(&mut result, boundary);
8690                            pending_boundary = Some(boundary);
8691                        }
8692                        FastDeferredNode::Concat {
8693                            prefix,
8694                            suffix: deferred_suffix,
8695                        } => {
8696                            pending.push(Frame::ContinuePrefix(prefix));
8697                            pending.push(Frame::Visit(deferred_suffix));
8698                        }
8699                    }
8700                }
8701                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
8702                Frame::FinishRule {
8703                    rule,
8704                    parent_suffix,
8705                    parent_alt_number,
8706                    parent_pending_boundary,
8707                } => {
8708                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
8709                        rule_index: rule.rule_index,
8710                        invoking_state: rule.invoking_state,
8711                        alt_number,
8712                        start_index: rule.start_index,
8713                        stop_index: rule.stop_index,
8714                        return_values: None,
8715                        children: result,
8716                    });
8717                    result = parent_suffix;
8718                    self.arena_prepend(&mut result, node);
8719                    alt_number = parent_alt_number;
8720                    pending_boundary = parent_pending_boundary;
8721                }
8722            }
8723        }
8724        (result, alt_number as usize)
8725    }
8726
8727    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) -> usize {
8728        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
8729        let (nodes, alt_number) =
8730            self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
8731        outcome.nodes = nodes;
8732        alt_number
8733    }
8734
8735    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
8736    /// heap work instead of the native call stack.
8737    fn recognize_repetition_fast(
8738        &mut self,
8739        atn: &Atn,
8740        request: &FastRecognizeRequest,
8741        shape: FastRepetitionShape,
8742        scratch: FastRecognizeScratch<'_, '_>,
8743    ) -> Vec<FastRecognizeOutcome> {
8744        let FastRecognizeScratch {
8745            predicate_context,
8746            visiting,
8747            memo,
8748            expected,
8749            native_depth,
8750        } = scratch;
8751        let lookahead = if self.fast_first_set_prefilter {
8752            atn.state(request.state_number).and_then(|state| {
8753                state
8754                    .rule_index()
8755                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8756                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
8757            })
8758        } else {
8759            None
8760        };
8761        let (enter_alt_number, exit_alt_number) = if self.fast_track_alt_numbers {
8762            let state = atn
8763                .state(request.state_number)
8764                .expect("repetition request state must exist");
8765            (
8766                next_alt_number(state, 2, shape.enter_transition_index, 0, true),
8767                next_alt_number(state, 2, shape.exit_transition_index, 0, true),
8768            )
8769        } else {
8770            (0, 0)
8771        };
8772        let mut work = Vec::with_capacity(2);
8773        push_fast_repetition_work(
8774            &mut work,
8775            shape,
8776            FastRepetitionPath {
8777                index: request.index,
8778                deferred_nodes: FastDeferredNodeId::EMPTY,
8779                diagnostics: DiagnosticSeqId::EMPTY,
8780                consumed_eof: false,
8781            },
8782            lookahead.as_deref(),
8783            self.token_type_at(request.index),
8784        );
8785        let mut coordinates = FastRepetitionCoordinates::new(request.index);
8786        let mut outcomes = Vec::new();
8787        while let Some(item) = work.pop() {
8788            match item {
8789                FastRepetitionWork::Enter(path) => {
8790                    if !coordinates.insert_entered(path) {
8791                        continue;
8792                    }
8793                    let path_nodes = if enter_alt_number == 0 {
8794                        path.deferred_nodes
8795                    } else {
8796                        let alternative = self
8797                            .recognition_arena
8798                            .deferred_alternative(enter_alt_number);
8799                        self.recognition_arena
8800                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8801                    };
8802                    let body_outcomes = self.recognize_state_fast(
8803                        atn,
8804                        FastRecognizeRequest {
8805                            state_number: shape.enter_target,
8806                            stop_state: shape.body_stop_state,
8807                            index: path.index,
8808                            rule_start_index: request.rule_start_index,
8809                            decision_start_index: request.decision_start_index,
8810                            precedence: request.precedence,
8811                            depth: request.depth.saturating_add(1),
8812                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8813                            recovery_state: request.recovery_state,
8814                        },
8815                        FastRecognizeScratch {
8816                            predicate_context,
8817                            visiting: &mut *visiting,
8818                            memo: &mut *memo,
8819                            expected: &mut *expected,
8820                            native_depth: native_depth + 1,
8821                        },
8822                    );
8823                    for body in body_outcomes.into_iter().rev() {
8824                        // ANTLR rejects nullable repetition bodies. Keep the
8825                        // interpreter bounded for malformed or recovered ATNs
8826                        // by mirroring the existing same-coordinate cycle cut.
8827                        if body.index <= path.index {
8828                            continue;
8829                        }
8830                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
8831                        let body_nodes = self
8832                            .recognition_arena
8833                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
8834                        let deferred_nodes = self
8835                            .recognition_arena
8836                            .concat_deferred_nodes(path_nodes, body_nodes);
8837                        let next_path = FastRepetitionPath {
8838                            index: body.index,
8839                            deferred_nodes,
8840                            diagnostics: self
8841                                .recognition_arena
8842                                .concat_diagnostics(path.diagnostics, body.diagnostics),
8843                            consumed_eof: path.consumed_eof || body.consumed_eof,
8844                        };
8845                        let symbol = self.token_type_at(next_path.index);
8846                        push_fast_repetition_work(
8847                            &mut work,
8848                            shape,
8849                            next_path,
8850                            lookahead.as_deref(),
8851                            symbol,
8852                        );
8853                    }
8854                }
8855                FastRepetitionWork::Exit(path) => {
8856                    if !coordinates.insert_exited(path) {
8857                        continue;
8858                    }
8859                    let path_nodes = if exit_alt_number == 0 {
8860                        path.deferred_nodes
8861                    } else {
8862                        let alternative =
8863                            self.recognition_arena.deferred_alternative(exit_alt_number);
8864                        self.recognition_arena
8865                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8866                    };
8867                    let suffixes = self.recognize_state_fast(
8868                        atn,
8869                        FastRecognizeRequest {
8870                            state_number: shape.exit_target,
8871                            stop_state: request.stop_state,
8872                            index: path.index,
8873                            rule_start_index: request.rule_start_index,
8874                            decision_start_index: request.decision_start_index,
8875                            precedence: request.precedence,
8876                            depth: request.depth.saturating_add(1),
8877                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8878                            recovery_state: request.recovery_state,
8879                        },
8880                        FastRecognizeScratch {
8881                            predicate_context,
8882                            visiting: &mut *visiting,
8883                            memo: &mut *memo,
8884                            expected: &mut *expected,
8885                            native_depth: native_depth + 1,
8886                        },
8887                    );
8888                    for mut outcome in suffixes {
8889                        outcome.deferred_nodes = self
8890                            .recognition_arena
8891                            .concat_deferred_nodes(path_nodes, outcome.deferred_nodes);
8892                        outcome.diagnostics = self
8893                            .recognition_arena
8894                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8895                        outcome.consumed_eof |= path.consumed_eof;
8896                        outcomes.push(outcome);
8897                    }
8898                }
8899            }
8900        }
8901        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8902        outcomes
8903    }
8904
8905    /// Attempts to reach `stop_state` from `state_number` without committing
8906    /// token consumption to the parser's public stream position.
8907    fn recognize_state_fast(
8908        &mut self,
8909        atn: &Atn,
8910        request: FastRecognizeRequest,
8911        scratch: FastRecognizeScratch<'_, '_>,
8912    ) -> Vec<FastRecognizeOutcome> {
8913        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
8914            return self.recognize_state_fast_inner(atn, request, scratch);
8915        }
8916        self.recognize_state_fast_checked(atn, request, scratch)
8917    }
8918
8919    #[inline(never)]
8920    fn recognize_state_fast_checked(
8921        &mut self,
8922        atn: &Atn,
8923        request: FastRecognizeRequest,
8924        mut scratch: FastRecognizeScratch<'_, '_>,
8925    ) -> Vec<FastRecognizeOutcome> {
8926        scratch.native_depth = 1;
8927        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
8928            self.recognize_state_fast_inner(atn, request, scratch)
8929        })
8930    }
8931
8932    #[allow(clippy::too_many_lines)]
8933    fn recognize_state_fast_inner(
8934        &mut self,
8935        atn: &Atn,
8936        request: FastRecognizeRequest,
8937        scratch: FastRecognizeScratch<'_, '_>,
8938    ) -> Vec<FastRecognizeOutcome> {
8939        #[cfg(feature = "perf-counters")]
8940        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8941        let FastRecognizeScratch {
8942            predicate_context,
8943            visiting,
8944            memo,
8945            expected,
8946            native_depth,
8947        } = scratch;
8948        let FastRecognizeRequest {
8949            mut state_number,
8950            stop_state,
8951            mut index,
8952            rule_start_index,
8953            decision_start_index,
8954            precedence,
8955            mut depth,
8956            recovery_symbols,
8957            recovery_state,
8958        } = request;
8959        let max_token_type = atn.max_token_type();
8960        // Walk straight-line epsilon chains in a loop instead of recursing
8961        // into `recognize_state_fast` for each intermediate state. ATN
8962        // serialization places long sequences of `BasicBlock` epsilon
8963        // transitions between decisions: turning that chain into a loop
8964        // collapses many recursive calls (and their memo lookups, vec
8965        // allocations, and visit-set churn) into a single function frame.
8966        // The loop exits as soon as we hit the original state's logic
8967        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8968        // precedence) so existing fanout, recovery, and memoization still
8969        // apply unchanged.
8970        //
8971        // The inline case also handles single-atom-match states on the
8972        // happy-pass path: when the lone consuming transition matches the
8973        // current lookahead, advance the index and continue without paying
8974        // for a full `recognize_state_fast` recursion. We track tokens we
8975        // consumed inline in `inline_consumed_tokens` so they can be
8976        // prepended onto the eventual outcome list once we hit a state
8977        // whose handling falls outside this fast loop.
8978        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8979        let mut inline_consumed_eof = false;
8980        loop {
8981            if depth > RECOGNITION_DEPTH_LIMIT {
8982                return Vec::new();
8983            }
8984            if state_number == stop_state {
8985                let mut nodes = NodeSeqId::EMPTY;
8986                if self.fast_token_nodes_enabled {
8987                    for token_index in inline_consumed_tokens.iter().rev() {
8988                        let token = self.arena_token_node(*token_index, false);
8989                        self.arena_prepend(&mut nodes, token);
8990                    }
8991                }
8992                return vec![FastRecognizeOutcome {
8993                    index,
8994                    consumed_eof: inline_consumed_eof,
8995                    diagnostics: DiagnosticSeqId::EMPTY,
8996                    deferred_nodes: FastDeferredNodeId::EMPTY,
8997                    nodes,
8998                }];
8999            }
9000            let Some(state) = atn.state(state_number) else {
9001                return Vec::new();
9002            };
9003            let transitions = state.transitions();
9004            if transitions.len() == 1 && !state.precedence_rule_decision() {
9005                let transition = transitions
9006                    .first()
9007                    .expect("single transition checked above");
9008                let transition_kind = transition.kind();
9009                let target = transition.target();
9010                match transition_kind {
9011                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
9012                        if left_recursive_boundary(atn, state, target).is_none() =>
9013                    {
9014                        #[cfg(feature = "perf-counters")]
9015                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9016                        state_number = target;
9017                        depth += 1;
9018                        continue;
9019                    }
9020                    ParserTransitionKind::Predicate
9021                        if left_recursive_boundary(atn, state, target).is_none() =>
9022                    {
9023                        #[cfg(feature = "perf-counters")]
9024                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9025                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
9026                        {
9027                            record_predicate_no_viable(expected, decision_start_index, index);
9028                            return Vec::new();
9029                        }
9030                        state_number = target;
9031                        depth += 1;
9032                        continue;
9033                    }
9034                    ParserTransitionKind::Precedence
9035                        if packed_i32(transition.arg0()) >= precedence
9036                            && left_recursive_boundary(atn, state, target).is_none() =>
9037                    {
9038                        #[cfg(feature = "perf-counters")]
9039                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9040                        state_number = target;
9041                        depth += 1;
9042                        continue;
9043                    }
9044                    // Single-atom / range / set / wildcard / not-set states
9045                    // are common (~17K of ~125K calls on C#) and almost
9046                    // always succeed in pass 1: no fanout, no recovery, no
9047                    // diagnostics. Inline the token match and continue
9048                    // walking instead of recursing — the recursive path
9049                    // would just allocate a Vec, build one outcome, prepend
9050                    // a Token node, and return. Skip pass 2 (recovery
9051                    // enabled): there the failure branch matters and the
9052                    // existing recursive code records expected symbols.
9053                    ParserTransitionKind::Atom
9054                    | ParserTransitionKind::Range
9055                    | ParserTransitionKind::Set
9056                    | ParserTransitionKind::NotSet
9057                    | ParserTransitionKind::Wildcard
9058                        if !self.fast_recovery_enabled =>
9059                    {
9060                        let symbol = self.token_type_at(index);
9061                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9062                            #[cfg(feature = "perf-counters")]
9063                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9064                            if self.fast_token_nodes_enabled {
9065                                inline_consumed_tokens.push(index);
9066                            }
9067                            inline_consumed_eof |= symbol == TOKEN_EOF;
9068                            index = self.consume_index(index, symbol);
9069                            state_number = target;
9070                            depth += 1;
9071                            continue;
9072                        }
9073                        // Fall through to break and let the regular
9074                        // body handle the no-match case (returns empty).
9075                    }
9076                    _ => {}
9077                }
9078            }
9079            break;
9080        }
9081        // If we collected token nodes inline but bail to the recursive
9082        // body (decision state, rule call, etc.), the outcomes returned
9083        // below will need those token nodes prepended.
9084        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
9085        let Some(state) = atn.state(state_number) else {
9086            return Vec::new();
9087        };
9088        let transitions = state.transitions();
9089        let transition_count = transitions.len();
9090        if !self.fast_recovery_enabled
9091            && let Some(shape) = fast_repetition_shape(atn, state)
9092        {
9093            let mut outcomes = self.recognize_repetition_fast(
9094                atn,
9095                &FastRecognizeRequest {
9096                    state_number,
9097                    stop_state,
9098                    index,
9099                    rule_start_index,
9100                    decision_start_index,
9101                    precedence,
9102                    depth,
9103                    recovery_symbols: Rc::clone(&recovery_symbols),
9104                    recovery_state,
9105                },
9106                shape,
9107                FastRecognizeScratch {
9108                    predicate_context,
9109                    visiting: &mut *visiting,
9110                    memo: &mut *memo,
9111                    expected: &mut *expected,
9112                    native_depth: native_depth + 1,
9113                },
9114            );
9115            if inline_pending {
9116                for outcome in &mut outcomes {
9117                    outcome.consumed_eof |= inline_consumed_eof;
9118                    if self.fast_token_nodes_enabled {
9119                        for token_index in inline_consumed_tokens.iter().rev() {
9120                            let token = self.arena_token_node(*token_index, false);
9121                            self.defer_fast_outcome_node(outcome, token);
9122                        }
9123                    }
9124                }
9125            }
9126            return outcomes;
9127        }
9128        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
9129        // fields and the rule/decision boundary indices are pure plumbing —
9130        // they only affect the recovery branch and the no-viable diagnostic
9131        // recording, neither of which fires when recovery is off. Zeroing
9132        // them in the memo key collapses calls that visit the same
9133        // `(state, index)` from different rule-call sites onto one cache
9134        // entry, which is the dominant cost on large grammars (e.g. C#) where
9135        // many rules eventually delegate into the same `expression` /
9136        // `primary_expression` / `type` branches.
9137        let key = if self.fast_recovery_enabled {
9138            FastRecognizeKey {
9139                state_number,
9140                stop_state,
9141                index,
9142                rule_start_index,
9143                decision_start_index,
9144                precedence,
9145                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
9146                recovery_state,
9147            }
9148        } else {
9149            FastRecognizeKey {
9150                state_number,
9151                stop_state,
9152                index,
9153                rule_start_index: 0,
9154                decision_start_index: None,
9155                precedence,
9156                recovery_symbols_id: 0,
9157                recovery_state: None,
9158            }
9159        };
9160        // Once the clean-pass probe has established that coordinates do not
9161        // repeat, stop paying for the full memo table. Recovery always keeps
9162        // memoization because cached failures carry diagnostics, while
9163        // repeat-heavy clean parses promote before reaching sparse mode.
9164        let memo_lookup_enabled = self.fast_recovery_enabled
9165            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
9166        if memo_lookup_enabled {
9167            if let Some(outcomes) = memo.get(&key) {
9168                #[cfg(feature = "perf-counters")]
9169                {
9170                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
9171                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
9172                }
9173                // Materialize a fresh `Vec` from the cached slice; the caller
9174                // mutates per-outcome state (eof flags, prepended nodes) so we
9175                // can't hand them the shared backing.
9176                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
9177                    let inline_eof = inline_consumed_eof;
9178                    let inline_tokens = &inline_consumed_tokens;
9179                    return outcomes
9180                        .iter()
9181                        .copied()
9182                        .map(|mut outcome| {
9183                            if inline_eof {
9184                                outcome.consumed_eof = true;
9185                            }
9186                            if self.fast_token_nodes_enabled {
9187                                for token_index in inline_tokens.iter().rev() {
9188                                    let token = self.arena_token_node(*token_index, false);
9189                                    self.defer_fast_outcome_node(&mut outcome, token);
9190                                }
9191                            }
9192                            outcome
9193                        })
9194                        .collect();
9195                }
9196                return outcomes.to_vec();
9197            }
9198            #[cfg(feature = "perf-counters")]
9199            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
9200        }
9201
9202        // Cycle detection: clean recognition keeps the narrow static cycle
9203        // guard used on hot paths. Recovery needs the broader epsilon-state
9204        // guard because an otherwise non-nullable loop body can recover as an
9205        // empty child at EOF and re-enter the loop at the same token.
9206        let needs_cycle_guard = if self.fast_recovery_enabled {
9207            transitions.iter().any(ParserTransition::is_epsilon)
9208        } else {
9209            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
9210        };
9211        #[cfg(feature = "perf-counters")]
9212        if needs_cycle_guard {
9213            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
9214        } else {
9215            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
9216            match state
9217                .transitions()
9218                .first()
9219                .expect("single-transition path requires one transition")
9220                .data()
9221            {
9222                Transition::Rule { .. } => {
9223                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
9224                }
9225                Transition::Atom { .. }
9226                | Transition::Range { .. }
9227                | Transition::Set { .. }
9228                | Transition::NotSet { .. }
9229                | Transition::Wildcard { .. } => {
9230                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
9231                }
9232                _ => {
9233                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
9234                }
9235            }
9236        }
9237        let has_inserted_cycle_guard = if needs_cycle_guard {
9238            if !visiting.insert(key.clone()) {
9239                #[cfg(feature = "perf-counters")]
9240                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
9241                return Vec::new();
9242            }
9243            true
9244        } else {
9245            false
9246        };
9247        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9248            Some(index)
9249        } else {
9250            decision_start_index
9251        };
9252        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
9253            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
9254        } else {
9255            (Rc::clone(&recovery_symbols), recovery_state)
9256        };
9257
9258        // Lookahead-based pruning. At a multi-alternative state we cache the
9259        // look-1 set of every outgoing transition; on visit we keep only the
9260        // transitions whose look-1 can accept the current lookahead (or that
9261        // can be reached without consuming and so could legitimately match a
9262        // shorter input). This is the main speedup vs. blind speculative
9263        // recursion: it lets each visit fan out only to the alternatives that
9264        // could possibly contribute a clean parse, mirroring the SLL phase of
9265        // ANTLR's adaptive prediction.
9266        //
9267        // Pruning is skipped at:
9268        //   * rule-start states (a child rule call may need every internal
9269        //     transition to surface single-token recovery diagnostics that
9270        //     ANTLR's reference parser emits at the rule's first consuming
9271        //     transition; the FIRST-set retry path turns the prefilter off
9272        //     entirely so let's keep this lightweight too),
9273        //   * left-recursive precedence loops (the precedence transition's
9274        //     gating is dynamic),
9275        //   * states with too few alternatives to benefit.
9276        let lookahead_filter = if transition_count > 1
9277            && self.fast_first_set_prefilter
9278            && !state.precedence_rule_decision()
9279            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
9280        {
9281            state
9282                .rule_index()
9283                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
9284                .map(|rule_stop| {
9285                    let symbol = self.token_type_at(index);
9286                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
9287                    (symbol, entry)
9288                })
9289        } else {
9290            None
9291        };
9292        // LL(1) fast path: when the FIRST sets for the decision are disjoint
9293        // and none is nullable, the lookahead deterministically selects one
9294        // alternative. The recursive recognizer can then commit to that single
9295        // alt without iterating every transition through `should_skip_via_lookahead`
9296        // — saving (transition_count - 1) filter probes per visit.
9297        //
9298        // Result is cached per `(state, lookahead_token)` on the parser
9299        // instance, so subsequent visits skip the FIRST-set scan entirely.
9300        let ll1_only_alt: Option<usize> = if transition_count > 1
9301            && let Some((symbol, entry)) = lookahead_filter.as_ref()
9302        {
9303            let key = (state.state_number(), *symbol);
9304            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
9305                cached
9306            } else {
9307                let result = ll1_unique_alt(entry, *symbol);
9308                self.ll1_decision_cache.insert(key, result);
9309                result
9310            }
9311        } else {
9312            None
9313        };
9314        let lookahead_filter = lookahead_filter.as_ref();
9315        // Pre-size only when we expect at least one outcome to land — most
9316        // single-transition fall-throughs (the loop above didn't catch
9317        // because they're atom/rule/predicate) push at most one entry, so
9318        // reserving one slot avoids a reallocation while keeping the
9319        // unused-slot waste at one element.
9320        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
9321        for (transition_index, transition) in transitions.iter().enumerate() {
9322            if let Some(alt) = ll1_only_alt {
9323                // LL(1) determinism: skip every alt except the chosen one.
9324                if alt != transition_index {
9325                    continue;
9326                }
9327            }
9328            let transition_kind = transition.kind();
9329            if ll1_only_alt.is_none()
9330                && should_skip_via_lookahead(
9331                    transition_kind,
9332                    transition_index,
9333                    lookahead_filter,
9334                    index,
9335                    self.fast_recovery_enabled,
9336                    expected,
9337                )
9338            {
9339                continue;
9340            }
9341            let target = transition.target();
9342            let outcomes_before_transition = outcomes.len();
9343            let left_recursive_boundary = match transition_kind {
9344                ParserTransitionKind::Epsilon
9345                | ParserTransitionKind::Action
9346                | ParserTransitionKind::Predicate
9347                | ParserTransitionKind::Precedence => left_recursive_boundary(atn, state, target),
9348                ParserTransitionKind::Atom
9349                | ParserTransitionKind::Range
9350                | ParserTransitionKind::Set
9351                | ParserTransitionKind::NotSet
9352                | ParserTransitionKind::Wildcard
9353                | ParserTransitionKind::Rule => None,
9354            };
9355            match transition_kind {
9356                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
9357                    #[cfg(feature = "perf-counters")]
9358                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9359                    outcomes.extend(self.recognize_state_fast(
9360                        atn,
9361                        FastRecognizeRequest {
9362                            state_number: target,
9363                            stop_state,
9364                            index,
9365                            rule_start_index,
9366                            decision_start_index: next_decision_start_index,
9367                            precedence,
9368                            depth: depth + 1,
9369                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9370                            recovery_state: epsilon_recovery_state,
9371                        },
9372                        FastRecognizeScratch {
9373                            predicate_context,
9374                            visiting,
9375                            memo,
9376                            expected,
9377                            native_depth: native_depth + 1,
9378                        },
9379                    ));
9380                }
9381                ParserTransitionKind::Predicate => {
9382                    #[cfg(feature = "perf-counters")]
9383                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9384                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
9385                        outcomes.extend(self.recognize_state_fast(
9386                            atn,
9387                            FastRecognizeRequest {
9388                                state_number: target,
9389                                stop_state,
9390                                index,
9391                                rule_start_index,
9392                                decision_start_index: next_decision_start_index,
9393                                precedence,
9394                                depth: depth + 1,
9395                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9396                                recovery_state: epsilon_recovery_state,
9397                            },
9398                            FastRecognizeScratch {
9399                                predicate_context,
9400                                visiting,
9401                                memo,
9402                                expected,
9403                                native_depth: native_depth + 1,
9404                            },
9405                        ));
9406                    } else {
9407                        record_predicate_no_viable(expected, next_decision_start_index, index);
9408                    }
9409                }
9410                ParserTransitionKind::Precedence => {
9411                    let transition_precedence = packed_i32(transition.arg0());
9412                    if transition_precedence >= precedence {
9413                        outcomes.extend(self.recognize_state_fast(
9414                            atn,
9415                            FastRecognizeRequest {
9416                                state_number: target,
9417                                stop_state,
9418                                index,
9419                                rule_start_index,
9420                                decision_start_index: next_decision_start_index,
9421                                precedence,
9422                                depth: depth + 1,
9423                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9424                                recovery_state: epsilon_recovery_state,
9425                            },
9426                            FastRecognizeScratch {
9427                                predicate_context,
9428                                visiting,
9429                                memo,
9430                                expected,
9431                                native_depth: native_depth + 1,
9432                            },
9433                        ));
9434                    }
9435                }
9436                ParserTransitionKind::Rule => {
9437                    let rule_index = transition.arg0() as usize;
9438                    let follow_state = transition.arg1() as usize;
9439                    let rule_precedence = packed_i32(transition.arg2());
9440                    #[cfg(feature = "perf-counters")]
9441                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
9442                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
9443                        continue;
9444                    };
9445                    // Lookahead-based pruning. The recognizer would otherwise
9446                    // explore every speculative rule call, producing exponential
9447                    // work on grammars with many epsilon-reachable rules. When
9448                    // the rule is non-nullable and its FIRST set excludes the
9449                    // current lookahead, recursion can't find a clean path
9450                    // *through this rule*. Skipping is only safe if some sibling
9451                    // transition can still consume the lookahead — otherwise the
9452                    // rule call is the sole continuation and must run so the
9453                    // single-token insertion / deletion recovery inside the
9454                    // called rule can fire (mirroring ANTLR's reference behavior
9455                    // of conjuring a missing token at child-rule entry).
9456                    let symbol = self.token_type_at(index);
9457                    if self.fast_first_set_prefilter {
9458                        // Probe the shared cross-parse cache first; build
9459                        // the entry on miss and intern it there. The
9460                        // computation is purely a function of the ATN, so
9461                        // the cached entry is reused across parses (and
9462                        // freshly-instantiated parser values that share
9463                        // the same `&'static Atn`).
9464                        //
9465                        // `rule_first_set` returns the computed entry
9466                        // directly — it intentionally skips inserting into
9467                        // the cache when the FIRST-set walk hit a cycle, so
9468                        // we cannot assume the entry is in the cache after
9469                        // computing it.
9470                        let first = self.cached_rule_first_set(atn, target, child_stop);
9471                        if should_skip_rule_via_first_set(
9472                            &first,
9473                            symbol,
9474                            self.fast_recovery_enabled,
9475                            index,
9476                            expected,
9477                        ) {
9478                            continue;
9479                        }
9480                    }
9481                    let expected_before_child =
9482                        self.fast_recovery_enabled.then(|| expected.clone());
9483                    let mut children = self.recognize_state_fast(
9484                        atn,
9485                        FastRecognizeRequest {
9486                            state_number: target,
9487                            stop_state: child_stop,
9488                            index,
9489                            rule_start_index: index,
9490                            decision_start_index: None,
9491                            precedence: rule_precedence,
9492                            depth: depth + 1,
9493                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9494                            recovery_state: epsilon_recovery_state,
9495                        },
9496                        FastRecognizeScratch {
9497                            predicate_context,
9498                            visiting,
9499                            memo,
9500                            expected,
9501                            native_depth: native_depth + 1,
9502                        },
9503                    );
9504                    if children.is_empty() && self.fast_recovery_enabled {
9505                        children = self.fast_child_rule_failure_recovery_outcomes(
9506                            FastChildRuleFailureRecoveryRequest {
9507                                atn,
9508                                rule_index,
9509                                start_index: index,
9510                                follow_state,
9511                                stop_state,
9512                                expected,
9513                            },
9514                        );
9515                    }
9516                    if let Some(expected_before_child) = expected_before_child {
9517                        if children
9518                            .iter()
9519                            .any(|child| child.diagnostics.is_empty() && child.index > index)
9520                        {
9521                            *expected = expected_before_child;
9522                        }
9523                    }
9524                    for child in children {
9525                        let child_index = child.index;
9526                        let child_consumed_eof = child.consumed_eof;
9527                        let child_diagnostics = child.diagnostics;
9528                        let empty_recovery = self.empty_recovery_symbols();
9529                        let follow_outcomes = self.recognize_state_fast(
9530                            atn,
9531                            FastRecognizeRequest {
9532                                state_number: follow_state,
9533                                stop_state,
9534                                index: child_index,
9535                                rule_start_index,
9536                                decision_start_index: next_decision_start_index,
9537                                precedence,
9538                                depth: depth + 1,
9539                                recovery_symbols: empty_recovery,
9540                                recovery_state: None,
9541                            },
9542                            FastRecognizeScratch {
9543                                predicate_context,
9544                                visiting,
9545                                memo,
9546                                expected,
9547                                native_depth: native_depth + 1,
9548                            },
9549                        );
9550                        if follow_outcomes.is_empty() {
9551                            continue;
9552                        }
9553                        let child_stop_index =
9554                            self.rule_stop_token_index(child_index, child_consumed_eof);
9555                        let child_node = self.build_parse_trees.then(|| {
9556                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
9557                                rule_index: u32::try_from(rule_index)
9558                                    .expect("rule index fits in u32"),
9559                                invoking_state: i32::try_from(invoking_state_number(state_number))
9560                                    .expect("invoking state fits in i32"),
9561                                start_index: u32::try_from(index)
9562                                    .expect("rule start index fits in u32"),
9563                                stop_index: child_stop_index.map(|stop_index| {
9564                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
9565                                }),
9566                                deferred_children: child.deferred_nodes,
9567                                children: child.nodes,
9568                            })
9569                        });
9570                        let child_diags_empty = child_diagnostics.is_empty();
9571                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
9572                            outcome.consumed_eof |= child_consumed_eof;
9573                            // Skip the prepend dance when there's nothing to
9574                            // merge from the child — common case in pass 1.
9575                            if !child_diags_empty {
9576                                outcome.diagnostics = self
9577                                    .recognition_arena
9578                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
9579                            }
9580                            if let Some(child_node) = child_node {
9581                                outcome.deferred_nodes = self
9582                                    .recognition_arena
9583                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
9584                            }
9585                            outcome
9586                        }));
9587                    }
9588                }
9589                ParserTransitionKind::Atom
9590                | ParserTransitionKind::Range
9591                | ParserTransitionKind::Set
9592                | ParserTransitionKind::NotSet
9593                | ParserTransitionKind::Wildcard => {
9594                    #[cfg(feature = "perf-counters")]
9595                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9596                    let symbol = self.token_type_at(index);
9597                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9598                        let next_index = self.consume_index(index, symbol);
9599                        let empty_recovery = self.empty_recovery_symbols();
9600                        outcomes.extend(
9601                            self.recognize_state_fast(
9602                                atn,
9603                                FastRecognizeRequest {
9604                                    state_number: target,
9605                                    stop_state,
9606                                    index: next_index,
9607                                    rule_start_index,
9608                                    decision_start_index: next_decision_start_index,
9609                                    precedence,
9610                                    depth: depth + 1,
9611                                    recovery_symbols: empty_recovery,
9612                                    recovery_state: None,
9613                                },
9614                                FastRecognizeScratch {
9615                                    predicate_context,
9616                                    visiting,
9617                                    memo,
9618                                    expected,
9619                                    native_depth: native_depth + 1,
9620                                },
9621                            )
9622                            .into_iter()
9623                            .map(|mut outcome| {
9624                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9625                                if self.fast_token_nodes_enabled {
9626                                    let token = self.arena_token_node(index, false);
9627                                    self.defer_fast_outcome_node(&mut outcome, token);
9628                                }
9629                                outcome
9630                            }),
9631                        );
9632                    } else {
9633                        if !self.fast_recovery_enabled {
9634                            // In pass 1 there is no recovery to attempt; the
9635                            // recovery branch below would never run, and the
9636                            // `expected_symbols` computation is just there
9637                            // to gate that branch. Skipping it eliminates
9638                            // ~1× `state_expected_symbols` lookup per failed
9639                            // atom transition (≈82K on mono-statement.cs)
9640                            // for zero observable behavior change.
9641                            continue;
9642                        }
9643                        let expected_symbols = fast_recovery_expected_symbols(
9644                            self,
9645                            atn,
9646                            state.state_number(),
9647                            &recovery_symbols,
9648                        );
9649                        if expected_symbols.contains(&symbol) {
9650                            continue;
9651                        }
9652                        {
9653                            expected.record_transition(index, transition, max_token_type);
9654                            record_no_viable_if_ambiguous(
9655                                expected,
9656                                next_decision_start_index,
9657                                index,
9658                            );
9659                            outcomes.extend(self.fast_single_token_deletion_recovery(
9660                                FastRecoveryRequest {
9661                                    atn,
9662                                    transition,
9663                                    expected_symbols: Rc::clone(&expected_symbols),
9664                                    target,
9665                                    request: FastRecognizeRequest {
9666                                        state_number,
9667                                        stop_state,
9668                                        index,
9669                                        rule_start_index,
9670                                        decision_start_index,
9671                                        precedence,
9672                                        depth,
9673                                        recovery_symbols: Rc::clone(&recovery_symbols),
9674                                        recovery_state,
9675                                    },
9676                                    visiting,
9677                                    memo,
9678                                    expected,
9679                                },
9680                                predicate_context,
9681                            ));
9682                            if !state_is_left_recursive_rule(atn, state) {
9683                                outcomes.extend(self.fast_single_token_insertion_recovery(
9684                                    FastRecoveryRequest {
9685                                        atn,
9686                                        transition,
9687                                        expected_symbols: Rc::clone(&expected_symbols),
9688                                        target,
9689                                        request: FastRecognizeRequest {
9690                                            state_number,
9691                                            stop_state,
9692                                            index,
9693                                            rule_start_index,
9694                                            decision_start_index,
9695                                            precedence,
9696                                            depth,
9697                                            recovery_symbols: Rc::clone(&recovery_symbols),
9698                                            recovery_state,
9699                                        },
9700                                        visiting,
9701                                        memo,
9702                                        expected,
9703                                    },
9704                                    predicate_context,
9705                                ));
9706                            }
9707                            outcomes.extend(self.fast_current_token_deletion_recovery(
9708                                FastCurrentTokenDeletionRequest {
9709                                    atn,
9710                                    expected_symbols,
9711                                    request: FastRecognizeRequest {
9712                                        state_number,
9713                                        stop_state,
9714                                        index,
9715                                        rule_start_index,
9716                                        decision_start_index,
9717                                        precedence,
9718                                        depth,
9719                                        recovery_symbols: Rc::clone(&recovery_symbols),
9720                                        recovery_state,
9721                                    },
9722                                    visiting,
9723                                    memo,
9724                                    expected,
9725                                },
9726                                predicate_context,
9727                            ));
9728                        }
9729                    }
9730                }
9731            }
9732            let alt_number = next_alt_number(
9733                state,
9734                transition_count,
9735                transition_index,
9736                0,
9737                self.fast_track_alt_numbers,
9738            );
9739            if alt_number != 0 || left_recursive_boundary.is_some() {
9740                for outcome in &mut outcomes[outcomes_before_transition..] {
9741                    if alt_number != 0 {
9742                        self.defer_fast_outcome_alternative(outcome, alt_number);
9743                    }
9744                    if let Some(rule_index) = left_recursive_boundary {
9745                        self.defer_fast_outcome_boundary(outcome, rule_index);
9746                    }
9747                }
9748            }
9749        }
9750
9751        if has_inserted_cycle_guard {
9752            visiting.remove(&key);
9753        }
9754        if matches!(
9755            self.prediction_mode,
9756            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9757        ) && self.fast_recovery_enabled
9758        {
9759            // Without recovery enabled every outcome already has empty
9760            // diagnostics, so the discard pass is a no-op — skipping it
9761            // saves an iter+retain on each of the ~1M visits.
9762            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
9763        }
9764        if self.fast_recovery_enabled {
9765            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
9766        } else {
9767            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
9768        }
9769        // Skip memoization for single-transition states whose outcome is
9770        // unambiguous: they only get re-entered if the caller revisits the
9771        // exact same call site, which is rare since the loop above already
9772        // collapsed straight-line epsilon walks. Multi-alternative states
9773        // are where backtracking actually revisits the same coordinate, so
9774        // we still memoize there. With recovery on we keep the existing
9775        // memoization unconditionally because the recovery branch may
9776        // record diagnostics that the cache must surface to repeated
9777        // failed visits.
9778        let should_memoize = self.fast_recovery_enabled
9779            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
9780        // Apply inline pending state to each outcome before returning.
9781        // Tokens consumed inline by the loop-collapse don't appear in the
9782        // recursive recognizer's output, so we need to prepend them here.
9783        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
9784            if inline_consumed_eof {
9785                outcome.consumed_eof = true;
9786            }
9787            if !inline_consumed_tokens.is_empty() {
9788                for token_index in inline_consumed_tokens.iter().rev() {
9789                    let token = self.arena_token_node(*token_index, false);
9790                    self.defer_fast_outcome_node(&mut outcome, token);
9791                }
9792            }
9793            outcome
9794        };
9795        if should_memoize {
9796            #[cfg(feature = "perf-counters")]
9797            {
9798                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
9799                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
9800                match outcomes.len() {
9801                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9802                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9803                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9804                }
9805            }
9806            // The memo is keyed by the loop-exit `(state_number, index)` so
9807            // the inline-consumed tokens belong to *this* call's output, not
9808            // the cached result. Memoize the bare outcomes (without the
9809            // inline-pending data), then prepend the inline data on return.
9810            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
9811            memo.insert(key, Rc::clone(&stored));
9812            if inline_pending {
9813                return stored
9814                    .iter()
9815                    .copied()
9816                    .map(&mut apply_inline_pending)
9817                    .collect();
9818            }
9819            return stored.to_vec();
9820        }
9821        #[cfg(feature = "perf-counters")]
9822        match outcomes.len() {
9823            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9824            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9825            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9826        }
9827        if inline_pending {
9828            return outcomes.into_iter().map(apply_inline_pending).collect();
9829        }
9830        outcomes
9831    }
9832
9833    /// Explores single-token deletion recovery while preserving the matched
9834    /// token and skipped error token in the selected parse tree path.
9835    fn single_token_deletion_recovery(
9836        &mut self,
9837        recovery: RecoveryRequest<'_, '_>,
9838    ) -> Vec<RecognizeOutcome> {
9839        let RecoveryRequest {
9840            atn,
9841            transition,
9842            expected_symbols,
9843            target,
9844            request,
9845            visiting,
9846            memo,
9847            expected,
9848        } = recovery;
9849        let RecognizeRequest {
9850            stop_state,
9851            index,
9852            rule_start_index,
9853            decision_start_index,
9854            init_action_rules,
9855            predicates,
9856            semantics,
9857            rule_args,
9858            member_actions,
9859            return_actions,
9860            local_int_arg,
9861            member_values,
9862            return_values,
9863            rule_alt_number,
9864            track_alt_numbers,
9865            consumed_eof,
9866            precedence,
9867            depth,
9868            ..
9869        } = request;
9870        let Some((diagnostic, next_index, next_symbol)) =
9871            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
9872        else {
9873            return Vec::new();
9874        };
9875        let after_next = self.consume_index(next_index, next_symbol);
9876        self.recognize_state(
9877            atn,
9878            RecognizeRequest {
9879                state_number: target,
9880                stop_state,
9881                index: after_next,
9882                rule_start_index,
9883                decision_start_index,
9884                init_action_rules,
9885                predicates,
9886                semantics,
9887                rule_args,
9888                member_actions,
9889                return_actions,
9890                local_int_arg,
9891                member_values,
9892                return_values,
9893                rule_alt_number,
9894                track_alt_numbers,
9895                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
9896                committed_decision: false,
9897                precedence,
9898                depth: depth + 1,
9899                recovery_symbols: BTreeSet::new(),
9900                recovery_state: None,
9901            },
9902            visiting,
9903            memo,
9904            expected,
9905        )
9906        .into_iter()
9907        .map(|mut outcome| {
9908            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9909            outcome.diagnostics = self
9910                .recognition_arena
9911                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9912            let token = self.arena_token_node(next_index, false);
9913            self.arena_prepend(&mut outcome.nodes, token);
9914            let error = self.arena_token_node(index, true);
9915            self.arena_prepend(&mut outcome.nodes, error);
9916            outcome
9917        })
9918        .collect()
9919    }
9920
9921    /// Retries the current recognition state after deleting one unexpected
9922    /// token, preserving the deleted token as an error node in the parse tree.
9923    fn current_token_deletion_recovery(
9924        &mut self,
9925        recovery: CurrentTokenDeletionRequest<'_, '_>,
9926    ) -> Vec<RecognizeOutcome> {
9927        let CurrentTokenDeletionRequest {
9928            atn,
9929            expected_symbols,
9930            mut request,
9931            visiting,
9932            memo,
9933            expected,
9934        } = recovery;
9935        let error_index = request.index;
9936        if error_index == request.rule_start_index {
9937            return Vec::new();
9938        }
9939        let Some((diagnostic, next_index, skipped)) =
9940            self.current_token_deletion(error_index, &expected_symbols)
9941        else {
9942            return Vec::new();
9943        };
9944        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9945        request.index = next_index;
9946        request.committed_decision = false;
9947        request.depth += 1;
9948        request.recovery_state = None;
9949        self.recognize_state(atn, request, visiting, memo, expected)
9950            .into_iter()
9951            .map(|mut outcome| {
9952                outcome.diagnostics = self
9953                    .recognition_arena
9954                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9955                for index in skipped.iter().rev() {
9956                    let error = self.arena_token_node(*index, true);
9957                    self.arena_prepend(&mut outcome.nodes, error);
9958                }
9959                outcome
9960            })
9961            .collect()
9962    }
9963
9964    /// Falls back after deletion/insertion repairs cannot continue from a
9965    /// failed consuming transition.
9966    fn consuming_failure_fallback(
9967        &mut self,
9968        fallback: ConsumingFailureFallback<'_>,
9969        visiting: &mut BTreeSet<RecognizeKey>,
9970        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9971        expected: &mut ExpectedTokens,
9972    ) -> Vec<RecognizeOutcome> {
9973        if fallback.expected_symbols.is_empty() {
9974            return Vec::new();
9975        }
9976        if fallback.symbol == TOKEN_EOF {
9977            return self.eof_consuming_failure_fallback(fallback, expected);
9978        }
9979        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9980    }
9981
9982    /// Keeps unexpected non-EOF input visible as an error node when no repair
9983    /// path can otherwise reach the transition target.
9984    fn non_eof_consuming_failure_fallback(
9985        &mut self,
9986        fallback: ConsumingFailureFallback<'_>,
9987        visiting: &mut BTreeSet<RecognizeKey>,
9988        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9989        expected: &mut ExpectedTokens,
9990    ) -> Vec<RecognizeOutcome> {
9991        let ConsumingFailureFallback {
9992            atn,
9993            target,
9994            request,
9995            symbol,
9996            expected_symbols,
9997            decision_start_index,
9998            decision,
9999        } = fallback;
10000        let error_index = request.index;
10001        let diagnostic =
10002            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
10003        let next_index = self.consume_index(error_index, symbol);
10004        self.recognize_state(
10005            atn,
10006            RecognizeRequest {
10007                state_number: target,
10008                stop_state: request.stop_state,
10009                index: next_index,
10010                rule_start_index: request.rule_start_index,
10011                decision_start_index,
10012                init_action_rules: request.init_action_rules,
10013                predicates: request.predicates,
10014                semantics: request.semantics,
10015                rule_args: request.rule_args,
10016                member_actions: request.member_actions,
10017                return_actions: request.return_actions,
10018                local_int_arg: request.local_int_arg,
10019                member_values: request.member_values,
10020                return_values: request.return_values,
10021                rule_alt_number: request.rule_alt_number,
10022                track_alt_numbers: request.track_alt_numbers,
10023                consumed_eof: request.consumed_eof,
10024                committed_decision: false,
10025                precedence: request.precedence,
10026                depth: request.depth + 1,
10027                recovery_symbols: BTreeSet::new(),
10028                recovery_state: None,
10029            },
10030            visiting,
10031            memo,
10032            expected,
10033        )
10034        .into_iter()
10035        .map(|mut outcome| {
10036            prepend_decision(&mut outcome, decision);
10037            outcome.diagnostics = self
10038                .recognition_arena
10039                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10040            let error = self.arena_token_node(error_index, true);
10041            self.arena_prepend(&mut outcome.nodes, error);
10042            outcome
10043        })
10044        .collect()
10045    }
10046
10047    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
10048    /// behavior of unwinding instead of inserting caller tokens at EOF.
10049    fn eof_consuming_failure_fallback(
10050        &mut self,
10051        fallback: ConsumingFailureFallback<'_>,
10052        expected: &ExpectedTokens,
10053    ) -> Vec<RecognizeOutcome> {
10054        let request = fallback.request;
10055        if request.index == request.rule_start_index {
10056            return Vec::new();
10057        }
10058        let diagnostic =
10059            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
10060        let diagnostics = self
10061            .recognition_arena
10062            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10063        vec![RecognizeOutcome {
10064            index: request.index,
10065            consumed_eof: request.consumed_eof,
10066            alt_number: request.rule_alt_number,
10067            member_values: request.member_values,
10068            return_values: request.return_values,
10069            diagnostics,
10070            decisions: Vec::new(),
10071            actions: Vec::new(),
10072            nodes: NodeSeqId::EMPTY,
10073        }]
10074    }
10075
10076    /// Explores single-token insertion recovery while adding a conjured
10077    /// missing-token error node to the selected parse tree path.
10078    fn single_token_insertion_recovery(
10079        &mut self,
10080        recovery: RecoveryRequest<'_, '_>,
10081    ) -> Vec<RecognizeOutcome> {
10082        let RecoveryRequest {
10083            atn,
10084            transition,
10085            expected_symbols,
10086            target,
10087            request,
10088            visiting,
10089            memo,
10090            expected,
10091        } = recovery;
10092        let RecognizeRequest {
10093            stop_state,
10094            index,
10095            rule_start_index,
10096            decision_start_index,
10097            init_action_rules,
10098            predicates,
10099            semantics,
10100            rule_args,
10101            member_actions,
10102            return_actions,
10103            local_int_arg,
10104            member_values,
10105            return_values,
10106            rule_alt_number,
10107            track_alt_numbers,
10108            consumed_eof,
10109            precedence,
10110            depth,
10111            ..
10112        } = request;
10113        let follow_symbols = state_expected_symbols(atn, transition.target());
10114        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
10115            transition,
10116            index,
10117            atn.max_token_type(),
10118            &expected_symbols,
10119            &follow_symbols,
10120        ) else {
10121            return Vec::new();
10122        };
10123        self.recognize_state(
10124            atn,
10125            RecognizeRequest {
10126                state_number: target,
10127                stop_state,
10128                index,
10129                rule_start_index,
10130                decision_start_index,
10131                init_action_rules,
10132                predicates,
10133                semantics,
10134                rule_args,
10135                member_actions,
10136                return_actions,
10137                local_int_arg,
10138                member_values,
10139                return_values,
10140                rule_alt_number,
10141                track_alt_numbers,
10142                consumed_eof,
10143                committed_decision: false,
10144                precedence,
10145                depth: depth + 1,
10146                recovery_symbols: BTreeSet::new(),
10147                recovery_state: None,
10148            },
10149            visiting,
10150            memo,
10151            expected,
10152        )
10153        .into_iter()
10154        .map(|mut outcome| {
10155            outcome.diagnostics = self
10156                .recognition_arena
10157                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10158            let missing = self.arena_missing_token_node(token_type, index, text.clone());
10159            self.arena_prepend(&mut outcome.nodes, missing);
10160            outcome
10161        })
10162        .collect()
10163    }
10164
10165    /// Attempts to reach `stop_state` and carries semantic actions for the
10166    /// selected parser path.
10167    #[allow(clippy::too_many_lines)]
10168    fn recognize_state(
10169        &mut self,
10170        atn: &Atn,
10171        request: RecognizeRequest<'_>,
10172        visiting: &mut BTreeSet<RecognizeKey>,
10173        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
10174        expected: &mut ExpectedTokens,
10175    ) -> Vec<RecognizeOutcome> {
10176        let request_template = request.clone();
10177        let RecognizeRequest {
10178            state_number,
10179            stop_state,
10180            index,
10181            rule_start_index,
10182            decision_start_index,
10183            init_action_rules,
10184            predicates,
10185            semantics,
10186            rule_args,
10187            member_actions,
10188            return_actions,
10189            local_int_arg,
10190            member_values,
10191            return_values,
10192            rule_alt_number,
10193            track_alt_numbers,
10194            consumed_eof,
10195            committed_decision,
10196            precedence,
10197            depth,
10198            recovery_symbols,
10199            recovery_state,
10200        } = request;
10201        if depth > RECOGNITION_DEPTH_LIMIT {
10202            return Vec::new();
10203        }
10204        if state_number == stop_state {
10205            return stop_outcome(
10206                index,
10207                consumed_eof,
10208                rule_alt_number,
10209                member_values,
10210                return_values,
10211            );
10212        }
10213        let key = RecognizeKey {
10214            state_number,
10215            stop_state,
10216            index,
10217            rule_start_index,
10218            decision_start_index,
10219            local_int_arg,
10220            member_values: member_values.clone(),
10221            return_values: return_values.clone(),
10222            rule_alt_number,
10223            track_alt_numbers,
10224            consumed_eof,
10225            committed_decision,
10226            precedence,
10227            recovery_symbols: recovery_symbols.clone(),
10228            recovery_state,
10229        };
10230        if let Some(outcomes) = memo.get(&key) {
10231            return outcomes.clone();
10232        }
10233
10234        let visit_key = key.clone();
10235        if !visiting.insert(visit_key.clone()) {
10236            return Vec::new();
10237        }
10238
10239        let Some(state) = atn.state(state_number) else {
10240            visiting.remove(&visit_key);
10241            return Vec::new();
10242        };
10243        let decision_override_generation = self.decision_override_generation;
10244        let transitions = state.transitions();
10245        let transition_count = transitions.len();
10246        let overridden_transition = if transition_count > 1
10247            && self.semantic_hooks.observes_parser_decisions()
10248        {
10249            atn.decision_to_state()
10250                .iter()
10251                .position(|candidate| candidate == state_number)
10252                .and_then(|decision| {
10253                    self.semantic_hooks
10254                        .parser_decision_override(decision, index, transition_count)
10255                })
10256                .and_then(|alternative| alternative.checked_sub(1))
10257                .filter(|alternative| *alternative < transition_count)
10258        } else {
10259            None
10260        };
10261        if overridden_transition.is_some() {
10262            self.decision_override_generation = self.decision_override_generation.wrapping_add(1);
10263        }
10264        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
10265            Some(index)
10266        } else {
10267            decision_start_index
10268        };
10269        let (epsilon_recovery_symbols, epsilon_recovery_state) =
10270            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
10271        let mut outcomes = Vec::new();
10272        for (transition_index, transition) in transitions.iter().enumerate() {
10273            if overridden_transition.is_some_and(|forced| forced != transition_index) {
10274                continue;
10275            }
10276            let transition_committed =
10277                committed_decision || overridden_transition == Some(transition_index);
10278            let mut transition_request = request_template.clone();
10279            transition_request.committed_decision = transition_committed;
10280            let decision =
10281                transition_decision(atn, state, transition_count, transition_index, predicates);
10282            let next_alt_number = next_alt_number(
10283                state,
10284                transition_count,
10285                transition_index,
10286                rule_alt_number,
10287                track_alt_numbers,
10288            );
10289            let transition_data = transition.data();
10290            match &transition_data {
10291                Transition::Epsilon { target } | Transition::Action { target, .. } => {
10292                    let action_rule_index = match &transition_data {
10293                        Transition::Action { rule_index, .. } => Some(*rule_index),
10294                        _ => None,
10295                    };
10296                    outcomes.extend(self.recognize_epsilon_or_action_step(
10297                        atn,
10298                        &transition_request,
10299                        EpsilonActionStep {
10300                            source_state: state_number,
10301                            target: *target,
10302                            action_rule_index,
10303                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
10304                            decision,
10305                            decision_start_index: next_decision_start_index,
10306                            alt_number: next_alt_number,
10307                            recovery_symbols: epsilon_recovery_symbols.clone(),
10308                            recovery_state: epsilon_recovery_state,
10309                        },
10310                        RecognizeScratch {
10311                            visiting,
10312                            memo,
10313                            expected,
10314                        },
10315                    ));
10316                }
10317                Transition::Predicate {
10318                    target,
10319                    rule_index,
10320                    pred_index,
10321                    ..
10322                } => {
10323                    let predicate = PredicateEval {
10324                        index,
10325                        rule_index: *rule_index,
10326                        pred_index: *pred_index,
10327                        predicates,
10328                        semantics,
10329                        context: None,
10330                        local_int_arg,
10331                        member_values: &member_values,
10332                    };
10333                    if self.parser_predicate_matches(predicate) {
10334                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
10335                        outcomes.extend(
10336                            self.recognize_state(
10337                                atn,
10338                                RecognizeRequest {
10339                                    state_number: *target,
10340                                    stop_state,
10341                                    index,
10342                                    rule_start_index,
10343                                    decision_start_index: next_decision_start_index,
10344                                    init_action_rules,
10345                                    predicates,
10346                                    semantics,
10347                                    rule_args,
10348                                    member_actions,
10349                                    return_actions,
10350                                    local_int_arg,
10351                                    member_values: member_values.clone(),
10352                                    return_values: return_values.clone(),
10353                                    rule_alt_number: next_alt_number,
10354                                    track_alt_numbers,
10355                                    consumed_eof,
10356                                    committed_decision: transition_committed,
10357                                    precedence,
10358                                    depth: depth + 1,
10359                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10360                                    recovery_state: epsilon_recovery_state,
10361                                },
10362                                visiting,
10363                                memo,
10364                                expected,
10365                            )
10366                            .into_iter()
10367                            .map(|mut outcome| {
10368                                prepend_decision(&mut outcome, decision);
10369                                if let Some(rule_index) = left_recursive_boundary {
10370                                    let boundary =
10371                                        self.arena_boundary_node(rule_index, next_alt_number);
10372                                    self.arena_prepend(&mut outcome.nodes, boundary);
10373                                }
10374                                outcome
10375                            }),
10376                        );
10377                    } else if let Some(message) = semantics
10378                        .and_then(|semantics| {
10379                            self.parser_semantic_ir_predicate_failure_message(
10380                                *rule_index,
10381                                *pred_index,
10382                                semantics,
10383                            )
10384                        })
10385                        .or_else(|| {
10386                            self.parser_predicate_failure_message(
10387                                *rule_index,
10388                                *pred_index,
10389                                predicates,
10390                            )
10391                        })
10392                    {
10393                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
10394                            rule_index: *rule_index,
10395                            index,
10396                            message,
10397                            member_values: member_values.clone(),
10398                            return_values: return_values.clone(),
10399                            rule_alt_number,
10400                        }));
10401                    } else {
10402                        record_predicate_no_viable(expected, next_decision_start_index, index);
10403                    }
10404                }
10405                Transition::Precedence {
10406                    target,
10407                    precedence: transition_precedence,
10408                } => {
10409                    if *transition_precedence >= precedence {
10410                        outcomes.extend(
10411                            self.recognize_state(
10412                                atn,
10413                                RecognizeRequest {
10414                                    state_number: *target,
10415                                    stop_state,
10416                                    index,
10417                                    rule_start_index,
10418                                    decision_start_index: next_decision_start_index,
10419                                    init_action_rules,
10420                                    predicates,
10421                                    semantics,
10422                                    rule_args,
10423                                    member_actions,
10424                                    return_actions,
10425                                    local_int_arg,
10426                                    member_values: member_values.clone(),
10427                                    return_values: return_values.clone(),
10428                                    rule_alt_number: next_alt_number,
10429                                    track_alt_numbers,
10430                                    consumed_eof,
10431                                    committed_decision: transition_committed,
10432                                    precedence,
10433                                    depth: depth + 1,
10434                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10435                                    recovery_state: epsilon_recovery_state,
10436                                },
10437                                visiting,
10438                                memo,
10439                                expected,
10440                            )
10441                            .into_iter()
10442                            .map(|mut outcome| {
10443                                prepend_decision(&mut outcome, decision);
10444                                outcome
10445                            }),
10446                        );
10447                    }
10448                }
10449                Transition::Rule {
10450                    target,
10451                    rule_index,
10452                    follow_state,
10453                    precedence: rule_precedence,
10454                    ..
10455                } => {
10456                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
10457                        continue;
10458                    };
10459                    let child_local_int_arg =
10460                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
10461                    let expected_before_child = expected.clone();
10462                    let children = self.recognize_state(
10463                        atn,
10464                        RecognizeRequest {
10465                            state_number: *target,
10466                            stop_state: child_stop,
10467                            index,
10468                            rule_start_index: index,
10469                            decision_start_index: None,
10470                            init_action_rules,
10471                            predicates,
10472                            semantics,
10473                            rule_args,
10474                            member_actions,
10475                            return_actions,
10476                            local_int_arg: child_local_int_arg,
10477                            member_values: member_values.clone(),
10478                            return_values: BTreeMap::new(),
10479                            rule_alt_number: 0,
10480                            track_alt_numbers,
10481                            consumed_eof: false,
10482                            committed_decision: transition_committed,
10483                            precedence: *rule_precedence,
10484                            depth: depth + 1,
10485                            recovery_symbols: epsilon_recovery_symbols.clone(),
10486                            recovery_state: epsilon_recovery_state,
10487                        },
10488                        visiting,
10489                        memo,
10490                        expected,
10491                    );
10492                    let children = if children.is_empty() {
10493                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
10494                            atn,
10495                            rule_index: *rule_index,
10496                            start_index: index,
10497                            follow_state: *follow_state,
10498                            stop_state,
10499                            member_values: member_values.clone(),
10500                            expected,
10501                        })
10502                    } else {
10503                        children
10504                    };
10505                    let preserve_child_expected =
10506                        self.child_expected_reaches_clean_eof(&children, expected);
10507                    restore_expected(
10508                        &children,
10509                        index,
10510                        expected,
10511                        expected_before_child,
10512                        preserve_child_expected,
10513                    );
10514                    for child in children {
10515                        let child_stop_index =
10516                            self.rule_stop_token_index(child.index, child.consumed_eof);
10517                        let child_nodes = self
10518                            .recognition_arena
10519                            .fold_left_recursive_boundaries(child.nodes);
10520                        let child_node = self.arena_rule_node(ArenaRuleSpec {
10521                            rule_index: *rule_index,
10522                            invoking_state: invoking_state_number(state_number),
10523                            alt_number: child.alt_number,
10524                            start_index: index,
10525                            stop_index: child_stop_index,
10526                            return_values: child.return_values.clone(),
10527                            children: child_nodes,
10528                        });
10529                        outcomes.extend(
10530                            self.recognize_state(
10531                                atn,
10532                                RecognizeRequest {
10533                                    state_number: *follow_state,
10534                                    stop_state,
10535                                    index: child.index,
10536                                    rule_start_index,
10537                                    decision_start_index: next_decision_start_index,
10538                                    init_action_rules,
10539                                    predicates,
10540                                    semantics,
10541                                    rule_args,
10542                                    member_actions,
10543                                    return_actions,
10544                                    local_int_arg,
10545                                    member_values: child.member_values.clone(),
10546                                    return_values: return_values.clone(),
10547                                    rule_alt_number,
10548                                    track_alt_numbers,
10549                                    consumed_eof: consumed_eof || child.consumed_eof,
10550                                    committed_decision: transition_committed
10551                                        && child.index == index,
10552                                    precedence,
10553                                    depth: depth + 1,
10554                                    recovery_symbols: BTreeSet::new(),
10555                                    recovery_state: None,
10556                                },
10557                                visiting,
10558                                memo,
10559                                expected,
10560                            )
10561                            .into_iter()
10562                            .map(|mut outcome| {
10563                                outcome.consumed_eof |= child.consumed_eof;
10564                                outcome.diagnostics = self
10565                                    .recognition_arena
10566                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
10567                                let mut decisions = child.decisions.clone();
10568                                decisions.append(&mut outcome.decisions);
10569                                outcome.decisions = decisions;
10570                                prepend_decision(&mut outcome, decision);
10571                                let mut actions = child.actions.clone();
10572                                if init_action_rules.contains(rule_index) {
10573                                    actions.insert(
10574                                        0,
10575                                        ParserAction::new_rule_init(
10576                                            *rule_index,
10577                                            index,
10578                                            Some(*follow_state),
10579                                        ),
10580                                    );
10581                                }
10582                                actions.append(&mut outcome.actions);
10583                                outcome.actions = actions;
10584                                self.arena_prepend(&mut outcome.nodes, child_node);
10585                                outcome
10586                            }),
10587                        );
10588                    }
10589                }
10590                Transition::Atom { target, .. }
10591                | Transition::Range { target, .. }
10592                | Transition::Set { target, .. }
10593                | Transition::NotSet { target, .. }
10594                | Transition::Wildcard { target, .. } => {
10595                    let symbol = self.token_type_at(index);
10596                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
10597                        let next_index = self.consume_index(index, symbol);
10598                        outcomes.extend(
10599                            self.recognize_state(
10600                                atn,
10601                                RecognizeRequest {
10602                                    state_number: *target,
10603                                    stop_state,
10604                                    index: next_index,
10605                                    rule_start_index,
10606                                    decision_start_index: next_decision_start_index,
10607                                    init_action_rules,
10608                                    predicates,
10609                                    semantics,
10610                                    rule_args,
10611                                    member_actions,
10612                                    return_actions,
10613                                    local_int_arg,
10614                                    member_values: member_values.clone(),
10615                                    return_values: return_values.clone(),
10616                                    rule_alt_number: next_alt_number,
10617                                    track_alt_numbers,
10618                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
10619                                    committed_decision: false,
10620                                    precedence,
10621                                    depth: depth + 1,
10622                                    recovery_symbols: BTreeSet::new(),
10623                                    recovery_state: None,
10624                                },
10625                                visiting,
10626                                memo,
10627                                expected,
10628                            )
10629                            .into_iter()
10630                            .map(|mut outcome| {
10631                                prepend_decision(&mut outcome, decision);
10632                                outcome.consumed_eof |= symbol == TOKEN_EOF;
10633                                let token = self.arena_token_node(index, false);
10634                                self.arena_prepend(&mut outcome.nodes, token);
10635                                outcome
10636                            }),
10637                        );
10638                    } else {
10639                        let expected_symbols =
10640                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
10641                        if expected_symbols.contains(&symbol) && !transition_committed {
10642                            continue;
10643                        }
10644                        expected.record_transition(index, transition, atn.max_token_type());
10645                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
10646                        let before_recovery = outcomes.len();
10647                        let recovery_request = transition_request.clone();
10648                        if transition_committed {
10649                            outcomes.extend(self.consuming_failure_fallback(
10650                                ConsumingFailureFallback {
10651                                    atn,
10652                                    target: *target,
10653                                    request: recovery_request,
10654                                    symbol,
10655                                    expected_symbols,
10656                                    decision_start_index: next_decision_start_index,
10657                                    decision,
10658                                },
10659                                visiting,
10660                                memo,
10661                                expected,
10662                            ));
10663                            break;
10664                        }
10665                        outcomes.extend(
10666                            self.single_token_deletion_recovery(RecoveryRequest {
10667                                atn,
10668                                transition,
10669                                expected_symbols: expected_symbols.clone(),
10670                                target: *target,
10671                                request: recovery_request.clone(),
10672                                visiting,
10673                                memo,
10674                                expected,
10675                            })
10676                            .into_iter()
10677                            .map(|mut outcome| {
10678                                prepend_decision(&mut outcome, decision);
10679                                outcome
10680                            }),
10681                        );
10682                        if !state_is_left_recursive_rule(atn, state) {
10683                            outcomes.extend(
10684                                self.single_token_insertion_recovery(RecoveryRequest {
10685                                    atn,
10686                                    transition,
10687                                    expected_symbols: expected_symbols.clone(),
10688                                    target: *target,
10689                                    request: recovery_request.clone(),
10690                                    visiting,
10691                                    memo,
10692                                    expected,
10693                                })
10694                                .into_iter()
10695                                .map(|mut outcome| {
10696                                    prepend_decision(&mut outcome, decision);
10697                                    outcome
10698                                }),
10699                            );
10700                        }
10701                        outcomes.extend(self.current_token_deletion_recovery(
10702                            CurrentTokenDeletionRequest {
10703                                atn,
10704                                expected_symbols: expected_symbols.clone(),
10705                                request: recovery_request.clone(),
10706                                visiting,
10707                                memo,
10708                                expected,
10709                            },
10710                        ));
10711                        if outcomes.len() == before_recovery {
10712                            outcomes.extend(self.consuming_failure_fallback(
10713                                ConsumingFailureFallback {
10714                                    atn,
10715                                    target: *target,
10716                                    request: recovery_request,
10717                                    symbol,
10718                                    expected_symbols,
10719                                    decision_start_index: next_decision_start_index,
10720                                    decision,
10721                                },
10722                                visiting,
10723                                memo,
10724                                expected,
10725                            ));
10726                        }
10727                    }
10728                }
10729            }
10730            if self.decision_override_generation != decision_override_generation {
10731                break;
10732            }
10733        }
10734
10735        visiting.remove(&visit_key);
10736        self.record_prediction_diagnostics(atn, state, index, &outcomes);
10737        if matches!(
10738            self.prediction_mode,
10739            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
10740        ) {
10741            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
10742        }
10743        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
10744        memo.insert(key, outcomes.clone());
10745        outcomes
10746    }
10747
10748    /// Follows an epsilon or semantic-action transition while preserving the
10749    /// path-local side effects that may later become generated action output.
10750    fn recognize_epsilon_or_action_step(
10751        &mut self,
10752        atn: &Atn,
10753        request: &RecognizeRequest<'_>,
10754        step: EpsilonActionStep,
10755        scratch: RecognizeScratch<'_>,
10756    ) -> Vec<RecognizeOutcome> {
10757        let RecognizeScratch {
10758            visiting,
10759            memo,
10760            expected,
10761        } = scratch;
10762        let action = step.action_rule_index.map(|rule_index| {
10763            ParserAction::new(
10764                step.source_state,
10765                rule_index,
10766                request.rule_start_index,
10767                self.rule_stop_token_index(request.index, request.consumed_eof),
10768            )
10769        });
10770        let next_member_values = if action.is_some() {
10771            member_values_after_action(
10772                step.source_state,
10773                request.member_actions,
10774                request.semantics,
10775                &request.member_values,
10776            )
10777        } else {
10778            request.member_values.clone()
10779        };
10780        let next_return_values = action.map_or_else(
10781            || request.return_values.clone(),
10782            |action| {
10783                return_values_after_action(
10784                    step.source_state,
10785                    action.rule_index(),
10786                    request.return_actions,
10787                    request.semantics,
10788                    &request.return_values,
10789                )
10790            },
10791        );
10792
10793        self.recognize_state(
10794            atn,
10795            RecognizeRequest {
10796                state_number: step.target,
10797                stop_state: request.stop_state,
10798                index: request.index,
10799                rule_start_index: request.rule_start_index,
10800                decision_start_index: step.decision_start_index,
10801                init_action_rules: request.init_action_rules,
10802                predicates: request.predicates,
10803                semantics: request.semantics,
10804                rule_args: request.rule_args,
10805                member_actions: request.member_actions,
10806                return_actions: request.return_actions,
10807                local_int_arg: request.local_int_arg,
10808                member_values: next_member_values,
10809                return_values: next_return_values,
10810                rule_alt_number: if step.left_recursive_boundary.is_some() {
10811                    0
10812                } else {
10813                    step.alt_number
10814                },
10815                track_alt_numbers: request.track_alt_numbers,
10816                consumed_eof: request.consumed_eof,
10817                committed_decision: request.committed_decision,
10818                precedence: request.precedence,
10819                depth: request.depth + 1,
10820                recovery_symbols: step.recovery_symbols,
10821                recovery_state: step.recovery_state,
10822            },
10823            visiting,
10824            memo,
10825            expected,
10826        )
10827        .into_iter()
10828        .map(|mut outcome| {
10829            prepend_decision(&mut outcome, step.decision);
10830            if let Some(rule_index) = step.left_recursive_boundary {
10831                let boundary = self.arena_boundary_node(rule_index, step.alt_number);
10832                self.arena_prepend(&mut outcome.nodes, boundary);
10833            }
10834            if let Some(action) = action {
10835                outcome.actions.insert(0, action);
10836            }
10837            outcome
10838        })
10839        .collect()
10840    }
10841
10842    /// Reads the token type at an absolute token-stream index without moving
10843    /// the parser's stream cursor. The fast recognizer probes lookahead at
10844    /// every state visit, so avoiding the seek round-trip is a measurable
10845    /// hot-path win on long inputs.
10846    fn token_type_at(&mut self, index: usize) -> i32 {
10847        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
10848            self.input.fill();
10849        }
10850        self.input.token_type_at_index(index)
10851    }
10852
10853    /// Returns the cached `state_expected_symbols` set for an ATN state.
10854    ///
10855    /// The fast recognizer consults this set on every state visit through
10856    /// `next_recovery_context`; the underlying DFS is a pure function of the
10857    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
10858    ///
10859    /// Caching is layered through `intern_recovery_symbols` so two ATN states
10860    /// with the same expected-symbol set share one `Rc`. That invariant is
10861    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
10862    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
10863    /// always interned before it ends up in a key.
10864    fn cached_state_expected_symbols(
10865        &mut self,
10866        atn: &Atn,
10867        state_number: usize,
10868    ) -> Rc<BTreeSet<i32>> {
10869        if let Some(cached) = self.state_expected_cache.get(&state_number) {
10870            return Rc::clone(cached);
10871        }
10872        let symbols = state_expected_symbols(atn, state_number);
10873        let entry = self.intern_recovery_symbols(symbols);
10874        self.state_expected_cache
10875            .insert(state_number, Rc::clone(&entry));
10876        entry
10877    }
10878
10879    fn cached_state_expected_token_set(
10880        &mut self,
10881        atn: &Atn,
10882        state_number: usize,
10883    ) -> Rc<TokenBitSet> {
10884        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
10885            return Rc::clone(cached);
10886        }
10887        // Purely a function of the ATN, so back the per-parser cache with the
10888        // thread-shared one — fresh parser instances (one per parse in
10889        // generated usage) start warm instead of rewalking the ATN.
10890        let symbols = with_shared_atn_caches(atn, |cache| {
10891            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
10892                return Rc::clone(cached);
10893            }
10894            let symbols = Rc::new(state_expected_token_set(atn, state_number));
10895            cache
10896                .state_expected_tokens
10897                .insert(state_number, Rc::clone(&symbols));
10898            symbols
10899        });
10900        self.state_expected_token_cache
10901            .insert(state_number, Rc::clone(&symbols));
10902        symbols
10903    }
10904
10905    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
10906        if self.rule_stop_reach_cache.len() <= state_number {
10907            self.rule_stop_reach_cache
10908                .resize_with(atn.states().len().max(state_number + 1), || None);
10909        }
10910        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
10911            return reaches;
10912        }
10913        let reaches = with_shared_atn_caches(atn, |cache| {
10914            *cache
10915                .rule_stop_reach
10916                .entry(state_number)
10917                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
10918        });
10919        self.rule_stop_reach_cache[state_number] = Some(reaches);
10920        reaches
10921    }
10922
10923    /// Returns the parser's empty `recovery_symbols` singleton so callers can
10924    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
10925    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
10926        Rc::clone(&self.empty_recovery_symbols)
10927    }
10928
10929    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
10930    /// recognizer can hash and compare keys by pointer.
10931    ///
10932    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
10933    /// come from this method or the empty singleton; otherwise two
10934    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
10935    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
10936    /// recognition coordinates.
10937    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
10938        if set.is_empty() {
10939            return Rc::clone(&self.empty_recovery_symbols);
10940        }
10941        let candidate = Rc::new(set);
10942        match self.recovery_symbols_intern.get(&candidate) {
10943            Some(existing) => Rc::clone(existing),
10944            None => {
10945                self.recovery_symbols_intern
10946                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
10947                candidate
10948            }
10949        }
10950    }
10951
10952    /// Returns the cached look-1 entry for a decision state, computing it on
10953    /// first use. Multi-alternative states are visited many times during
10954    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
10955    /// hash lookup per visit.
10956    fn cached_decision_lookahead(
10957        &mut self,
10958        atn: &Atn,
10959        state: AtnState<'_>,
10960        rule_stop_state: usize,
10961    ) -> Rc<DecisionLookahead> {
10962        // Hit the parser-instance cache first. Decision lookahead is purely
10963        // a function of the ATN/state, so on a warm cache we skip the
10964        // thread-local + RefCell + HashMap-entry dance through
10965        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
10966        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
10967        // hashmap-entry overhead in profiles.
10968        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
10969            return Rc::clone(cached);
10970        }
10971        let entry = with_shared_atn_caches(atn, |cache| {
10972            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10973                return Rc::clone(cached);
10974            }
10975            let mut entry = DecisionLookahead {
10976                transitions: Vec::with_capacity(state.transitions().len()),
10977            };
10978            for transition in &state.transitions() {
10979                entry.transitions.push(transition_first_set(
10980                    atn,
10981                    transition,
10982                    rule_stop_state,
10983                    &mut cache.first_set,
10984                ));
10985            }
10986            let entry = Rc::new(entry);
10987            cache
10988                .decision_lookahead
10989                .insert(state.state_number(), Rc::clone(&entry));
10990            entry
10991        });
10992        self.decision_lookahead_cache
10993            .insert(state.state_number(), Rc::clone(&entry));
10994        entry
10995    }
10996
10997    fn cached_rule_first_set(
10998        &mut self,
10999        atn: &Atn,
11000        target: usize,
11001        child_stop: usize,
11002    ) -> Rc<FirstSet> {
11003        if self.rule_first_set_cache.len() <= target {
11004            self.rule_first_set_cache
11005                .resize_with(atn.states().len().max(target + 1), || None);
11006        }
11007        if let Some(cached) = self
11008            .rule_first_set_cache
11009            .get(target)
11010            .and_then(Option::as_ref)
11011        {
11012            return Rc::clone(cached);
11013        }
11014        let first = with_shared_first_set_cache(atn, |cache| {
11015            rule_first_set(atn, target, child_stop, cache)
11016        });
11017        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
11018        first
11019    }
11020
11021    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
11022        let atn_key = SharedAtnCacheKey::for_atn(atn);
11023        if self.empty_cycle_cache_atn != Some(atn_key) {
11024            self.empty_cycle_cache.clear();
11025            self.empty_cycle_cache_atn = Some(atn_key);
11026        }
11027        if self.empty_cycle_cache.len() <= state_number {
11028            self.empty_cycle_cache
11029                .resize_with(atn.state_count().max(state_number + 1), || None);
11030        }
11031        if let Some(cached) = self.empty_cycle_cache[state_number] {
11032            return cached;
11033        }
11034        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
11035        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
11036        self.empty_cycle_cache[state_number] = Some(result);
11037        result
11038    }
11039
11040    fn empty_path_reaches_state(
11041        &mut self,
11042        atn: &Atn,
11043        state_number: usize,
11044        target_state: usize,
11045        visited: &mut FxHashSet<usize>,
11046    ) -> bool {
11047        enum Work {
11048            Visit(usize),
11049            RuleFollow {
11050                target: usize,
11051                rule_index: usize,
11052                follow_state: usize,
11053            },
11054        }
11055
11056        let mut work = vec![Work::Visit(state_number)];
11057        while let Some(item) = work.pop() {
11058            match item {
11059                Work::Visit(state_number) => {
11060                    if !visited.insert(state_number) {
11061                        continue;
11062                    }
11063                    let Some(state) = atn.state(state_number) else {
11064                        continue;
11065                    };
11066                    let transitions = state.transitions();
11067                    for transition_index in (0..transitions.len()).rev() {
11068                        let transition = transitions
11069                            .get(transition_index)
11070                            .expect("in-bounds parser transition");
11071                        let kind = transition.kind();
11072                        let target = transition.target();
11073                        match kind {
11074                            ParserTransitionKind::Atom
11075                            | ParserTransitionKind::Range
11076                            | ParserTransitionKind::Set
11077                            | ParserTransitionKind::NotSet
11078                            | ParserTransitionKind::Wildcard => {}
11079                            ParserTransitionKind::Rule => {
11080                                if target == target_state {
11081                                    return true;
11082                                }
11083                                work.push(Work::RuleFollow {
11084                                    target,
11085                                    rule_index: transition.arg0() as usize,
11086                                    follow_state: transition.arg1() as usize,
11087                                });
11088                                work.push(Work::Visit(target));
11089                            }
11090                            ParserTransitionKind::Epsilon
11091                            | ParserTransitionKind::Predicate
11092                            | ParserTransitionKind::Action
11093                            | ParserTransitionKind::Precedence => {
11094                                if target == target_state {
11095                                    return true;
11096                                }
11097                                work.push(Work::Visit(target));
11098                            }
11099                        }
11100                    }
11101                }
11102                Work::RuleFollow {
11103                    target,
11104                    rule_index,
11105                    follow_state,
11106                } => {
11107                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
11108                        continue;
11109                    };
11110                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
11111                        if follow_state == target_state {
11112                            return true;
11113                        }
11114                        work.push(Work::Visit(follow_state));
11115                    }
11116                }
11117            }
11118        }
11119        false
11120    }
11121
11122    /// Decides whether the clean recognizer should use its full outcome memo
11123    /// table for this coordinate.
11124    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
11125        match self.clean_memo_mode {
11126            CleanMemoMode::Promote => true,
11127            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
11128            CleanMemoMode::Sparse => {
11129                self.clean_memo_sparse_samples += 1;
11130                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
11131                    return false;
11132                }
11133                self.clean_memo_sparse_samples = 0;
11134                self.clean_memo_mode = CleanMemoMode::Probe;
11135                self.clean_memo_probe_samples = 0;
11136                self.clean_memo_probe_repeats = 0;
11137                self.clean_memo_probe_seen.clear();
11138                self.observe_clean_memo_probe(key)
11139            }
11140        }
11141    }
11142
11143    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
11144        self.clean_memo_probe_samples += 1;
11145        if !self.clean_memo_probe_seen.insert(key.clone()) {
11146            self.clean_memo_probe_repeats += 1;
11147        }
11148        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
11149            self.clean_memo_mode = CleanMemoMode::Promote;
11150            self.clean_memo_probe_seen.clear();
11151            return true;
11152        }
11153        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
11154            self.clean_memo_mode = CleanMemoMode::Sparse;
11155            self.clean_memo_sparse_samples = 0;
11156            self.clean_memo_probe_seen.clear();
11157            return false;
11158        }
11159        true
11160    }
11161
11162    /// Borrows the visible token at an absolute token-stream index.
11163    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
11164        self.input.get(index)
11165    }
11166
11167    /// Returns the compact token ID at an absolute token-stream index.
11168    fn token_id_at(&self, index: usize) -> Option<TokenId> {
11169        self.input.get_id(index)
11170    }
11171
11172    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
11173        let token = self
11174            .token_id_at(index)
11175            .expect("recognized token index must exist in the token store");
11176        let node = if error {
11177            ArenaRecognizedNode::ErrorToken { token }
11178        } else {
11179            ArenaRecognizedNode::Token { token }
11180        };
11181        self.recognition_arena.push_node(node)
11182    }
11183
11184    fn arena_missing_token_node(
11185        &mut self,
11186        token_type: i32,
11187        at_index: usize,
11188        text: String,
11189    ) -> RecognizedNodeId {
11190        let extra = self
11191            .recognition_arena
11192            .push_extra(RecognitionExtra::MissingToken {
11193                token_type,
11194                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
11195                text,
11196            });
11197        self.recognition_arena
11198            .push_node(ArenaRecognizedNode::MissingToken { extra })
11199    }
11200
11201    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
11202        let ArenaRuleSpec {
11203            rule_index,
11204            invoking_state,
11205            alt_number,
11206            start_index,
11207            stop_index,
11208            return_values,
11209            children,
11210        } = spec;
11211        let return_values = (!return_values.is_empty()).then(|| {
11212            self.recognition_arena
11213                .push_extra(RecognitionExtra::ReturnValues(return_values))
11214        });
11215        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
11216            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11217            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
11218            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11219            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
11220            stop_index: stop_index
11221                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
11222            return_values,
11223            children,
11224        })
11225    }
11226
11227    fn arena_boundary_node(&mut self, rule_index: usize, alt_number: usize) -> RecognizedNodeId {
11228        self.recognition_arena
11229            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
11230                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11231                alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11232            })
11233    }
11234
11235    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
11236        *sequence = self.recognition_arena.prepend(*sequence, node);
11237    }
11238
11239    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
11240        self.last_recognition_arena_root = root;
11241        self.last_recognition_arena_diagnostics = diagnostics;
11242        #[cfg(feature = "perf-counters")]
11243        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
11244            let stats = self.recognition_arena_stats();
11245            #[allow(clippy::print_stderr)]
11246            {
11247                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
11248                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
11249                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
11250                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
11251                eprintln!("perf recognition_links_total={}", stats.total_links);
11252                eprintln!("perf recognition_links_live={}", stats.live_links);
11253                eprintln!("perf recognition_links_dead={}", stats.dead_links);
11254                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
11255                eprintln!("perf recognition_extras_total={}", stats.total_extras);
11256                eprintln!("perf recognition_extras_live={}", stats.live_extras);
11257                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
11258                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
11259            }
11260        }
11261    }
11262
11263    fn reset_recognition_arena(&mut self) {
11264        self.recognition_arena.reset();
11265        self.last_recognition_arena_root = NodeSeqId::EMPTY;
11266        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
11267    }
11268
11269    /// Normalizes the current token-stream cursor to the next parser-visible
11270    /// token before capturing a rule start boundary.
11271    fn current_visible_index(&mut self) -> usize {
11272        let index = self.input.index();
11273        self.input.seek(index);
11274        self.input.index()
11275    }
11276
11277    /// Reports whether a child rule reached EOF cleanly while also recording
11278    /// an EOF expectation from a longer path inside that child.
11279    fn child_expected_reaches_clean_eof(
11280        &mut self,
11281        children: &[RecognizeOutcome],
11282        expected: &ExpectedTokens,
11283    ) -> bool {
11284        let Some(index) = expected.index else {
11285            return false;
11286        };
11287        self.token_type_at(index) == TOKEN_EOF
11288            && children
11289                .iter()
11290                .any(|child| child.diagnostics.is_empty() && child.index == index)
11291    }
11292
11293    /// Finds the previous token visible to the parser before `index`.
11294    ///
11295    /// The token stream cursor skips hidden-channel tokens, so subtracting one
11296    /// from a visible-token index can point at whitespace. Parser intervals use
11297    /// this helper to stop at the previous visible token while preserving hidden
11298    /// text inside the rendered interval.
11299    fn previous_token_index(&self, index: usize) -> Option<usize> {
11300        self.input.previous_visible_token_index(index)
11301    }
11302
11303    /// Returns the token-stream index used as a rule stop boundary.
11304    ///
11305    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
11306    /// stop at `index` rather than at the previous visible token.
11307    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
11308        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
11309            Some(index)
11310        } else {
11311            self.previous_token_index(index)
11312        }
11313    }
11314
11315    /// Stop-token index for a rule's `@after` action, matching the boundary that
11316    /// `finish_rule` records on the rule context.
11317    ///
11318    /// A rule that matched EOF leaves the cursor parked on the EOF token
11319    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
11320    /// the current index rather than the previous visible token. Without this,
11321    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
11322    /// report the token before EOF (or `None` for empty input), diverging from
11323    /// the rule context that `finish_rule` builds.
11324    ///
11325    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
11326    /// rule ends right before EOF without matching it (the cursor is parked on
11327    /// EOF, but the rule did not consume it). Prefer
11328    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
11329    /// rule context already recorded with the real flag. Kept for callers without
11330    /// the rule tree in hand.
11331    #[must_use]
11332    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
11333        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
11334        self.rule_stop_token_index(current_index, consumed_eof)
11335    }
11336
11337    /// Stop-token index for a rule's `@after` action, taken from the stop token
11338    /// the rule context already recorded.
11339    ///
11340    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
11341    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
11342    /// the rule context — even when the rule ends immediately before EOF without
11343    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
11344    /// to the cursor-based inference only when the tree carries no rule stop.
11345    #[must_use]
11346    pub fn after_action_stop_index_for_tree(
11347        &mut self,
11348        tree: ParseTree,
11349        current_index: usize,
11350    ) -> Option<usize> {
11351        if let Some(stop) = self
11352            .node(tree)
11353            .as_rule()
11354            .and_then(crate::tree::RuleNodeView::stop_id)
11355        {
11356            return Some(stop.index());
11357        }
11358        self.after_action_stop_index(current_index)
11359    }
11360
11361    /// Start-token index for a rule's `@after` action, taken from the start token
11362    /// the rule context already recorded.
11363    ///
11364    /// `enter_rule` sets the rule context start to the first visible token (it
11365    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
11366    /// `$text` in an `@after` action aligned with the rule context — even when the
11367    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
11368    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
11369    /// the tree carries no rule start.
11370    #[must_use]
11371    pub fn after_action_start_index_for_tree(
11372        &self,
11373        tree: ParseTree,
11374        fallback_index: usize,
11375    ) -> usize {
11376        if let Some(start) = self
11377            .node(tree)
11378            .as_rule()
11379            .and_then(crate::tree::RuleNodeView::start_id)
11380        {
11381            return start.index();
11382        }
11383        fallback_index
11384    }
11385
11386    /// Returns the rule stop token for a selected parse path.
11387    ///
11388    /// EOF transitions do not advance the token-stream cursor, so an EOF match
11389    /// must use the current token rather than the previous visible token.
11390    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
11391        self.rule_stop_token_index(index, consumed_eof)
11392            .and_then(|token_index| self.token_id_at(token_index))
11393    }
11394
11395    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
11396    ///
11397    /// Generated Java reports the failed-predicate message at the current
11398    /// lookahead, then consumes until rule recovery can resume. The metadata
11399    /// runtime models the same visible tree shape by keeping skipped tokens as
11400    /// error nodes and returning from the active rule at EOF.
11401    fn predicate_failure_recovery(
11402        &mut self,
11403        request: PredicateFailureRecovery<'_>,
11404    ) -> RecognizeOutcome {
11405        let PredicateFailureRecovery {
11406            rule_index,
11407            index,
11408            message,
11409            member_values,
11410            return_values,
11411            rule_alt_number,
11412        } = request;
11413        let rule_name = self
11414            .rule_names()
11415            .get(rule_index)
11416            .map_or_else(|| rule_index.to_string(), Clone::clone);
11417        let diagnostic = diagnostic_for_token(
11418            self.token_at(index).as_ref(),
11419            format!("rule {rule_name} {message}"),
11420        );
11421        let mut reversed_nodes = NodeSeqId::EMPTY;
11422        let mut next_index = index;
11423        loop {
11424            let symbol = self.token_type_at(next_index);
11425            if symbol == TOKEN_EOF {
11426                break;
11427            }
11428            let error = self.arena_token_node(next_index, true);
11429            self.arena_prepend(&mut reversed_nodes, error);
11430            let after = self.consume_index(next_index, symbol);
11431            if after == next_index {
11432                break;
11433            }
11434            next_index = after;
11435        }
11436        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
11437        let diagnostics = self
11438            .recognition_arena
11439            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
11440        RecognizeOutcome {
11441            index: next_index,
11442            consumed_eof: false,
11443            alt_number: rule_alt_number,
11444            member_values,
11445            return_values,
11446            diagnostics,
11447            decisions: Vec::new(),
11448            actions: Vec::new(),
11449            nodes,
11450        }
11451    }
11452
11453    /// Evaluates a user hook for a predicate coordinate that has no generated
11454    /// runtime table entry.
11455    fn parser_semantic_hook_result(
11456        &mut self,
11457        request: ParserSemanticHookRequest<'_>,
11458    ) -> Option<bool> {
11459        let ParserSemanticHookRequest {
11460            index,
11461            rule_index,
11462            pred_index,
11463            context,
11464            local_int_arg,
11465            member_values,
11466        } = request;
11467        let rule_name = self.rule_names().get(rule_index).cloned();
11468        self.input.seek(index);
11469        let input = &mut self.input;
11470        let semantic_hooks = &mut self.semantic_hooks;
11471        let mut ctx = ParserSemCtx {
11472            input,
11473            tree_storage: &self.tree,
11474            rule_index,
11475            coordinate_index: pred_index,
11476            rule_name,
11477            context,
11478            tree: None,
11479            local_int_arg,
11480            member_values,
11481            action: None,
11482        };
11483        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
11484    }
11485
11486    /// Re-inserts unknown-predicate coordinates recorded before a nested
11487    /// interpreted recognition, preserving order and skipping any the nested
11488    /// call already recorded, so a generated parent's fail-loud coordinates
11489    /// survive descending into an interpreted child.
11490    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
11491        if prior.is_empty() {
11492            return;
11493        }
11494        let mut merged = prior;
11495        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
11496            if !merged.contains(&coordinate) {
11497                merged.push(coordinate);
11498            }
11499        }
11500        self.unknown_predicate_hits = merged;
11501    }
11502
11503    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
11504    /// that has no entry in the generated predicate table.
11505    ///
11506    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
11507    /// the guarded path is abandoned; the parse entry surfaces the recorded
11508    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
11509    /// because a parse that consulted an unknown predicate is unreliable no
11510    /// matter which paths were ultimately selected.
11511    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
11512        apply_unknown_predicate_policy(
11513            self.unknown_predicate_policy,
11514            rule_index,
11515            pred_index,
11516            &mut self.unknown_predicate_hits,
11517        )
11518    }
11519
11520    /// Builds the fail-loud error for unknown predicate coordinates recorded
11521    /// by the current parse, if any.
11522    fn unknown_semantic_error(&self) -> Option<AntlrError> {
11523        use std::fmt::Write as _;
11524        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
11525            return None;
11526        }
11527        let mut message = String::new();
11528        for (rule_index, pred_index) in &self.unknown_predicate_hits {
11529            if !message.is_empty() {
11530                message.push_str("; ");
11531            }
11532            let _ = match self.rule_names().get(*rule_index) {
11533                Some(rule_name) => write!(
11534                    message,
11535                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
11536                ),
11537                None => write!(
11538                    message,
11539                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
11540                ),
11541            };
11542        }
11543        for (rule_index, source_state) in &self.unhandled_action_hits {
11544            if !message.is_empty() {
11545                message.push_str("; ");
11546            }
11547            let _ = match self.rule_names().get(*rule_index) {
11548                Some(rule_name) => write!(
11549                    message,
11550                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
11551                ),
11552                None => write!(
11553                    message,
11554                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
11555                ),
11556            };
11557        }
11558        Some(AntlrError::Unsupported(message))
11559    }
11560
11561    /// Evaluates one lowered predicate expression at the requested input
11562    /// position.
11563    ///
11564    /// This sits in the prediction hot loop, so the context borrows the
11565    /// speculative member state read-only and the rule name by reference —
11566    /// no per-evaluation allocation. Only the hook escape path materializes
11567    /// owned copies, and only when a hook is actually consulted.
11568    fn parser_semir_predicate_matches(
11569        &mut self,
11570        semantics: &ParserSemantics,
11571        predicate: &ParserSemanticPredicate,
11572        request: ParserSemanticHookRequest<'_>,
11573    ) -> bool {
11574        self.input.seek(request.index);
11575        let rule_name = self
11576            .data
11577            .rule_names()
11578            .get(request.rule_index)
11579            .map(String::as_str);
11580        let unknown_predicate_policy = self.unknown_predicate_policy;
11581        let mut ctx = ParserSemIrCtx {
11582            input: &mut self.input,
11583            tree_storage: &self.tree,
11584            semantic_hooks: &mut self.semantic_hooks,
11585            rule_index: request.rule_index,
11586            coordinate_index: request.pred_index,
11587            rule_name,
11588            context: request.context,
11589            local_int_arg: request.local_int_arg,
11590            member_values: request.member_values,
11591            invoked_predicates: &mut self.invoked_predicates,
11592            unknown_predicate_policy,
11593            unknown_predicate_hits: &mut self.unknown_predicate_hits,
11594        };
11595        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
11596    }
11597
11598    fn fast_parser_predicate_matches(
11599        &mut self,
11600        context: Option<FastPredicateContext<'_>>,
11601        transition: ParserTransition<'_>,
11602        index: usize,
11603    ) -> bool {
11604        let Some(context) = context else {
11605            return true;
11606        };
11607        let rule_index = transition.arg0() as usize;
11608        let pred_index = transition.arg1() as usize;
11609        let key = (index, rule_index, pred_index);
11610        if let Some(result) = self.fast_predicate_cache.get(&key) {
11611            return *result;
11612        }
11613        let result = self.parser_predicate_matches(PredicateEval {
11614            index,
11615            rule_index,
11616            pred_index,
11617            predicates: context.predicates,
11618            semantics: context.semantics,
11619            context: None,
11620            local_int_arg: None,
11621            member_values: context.member_values,
11622        });
11623        self.fast_predicate_cache.insert(key, result);
11624        result
11625    }
11626
11627    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
11628        let PredicateEval {
11629            index,
11630            rule_index,
11631            pred_index,
11632            predicates,
11633            semantics,
11634            context,
11635            local_int_arg,
11636            member_values,
11637        } = eval;
11638        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
11639            semantics
11640                .predicates
11641                .iter()
11642                .find(|predicate| {
11643                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
11644                })
11645                .map(|predicate| (semantics, predicate))
11646        }) {
11647            return self.parser_semir_predicate_matches(
11648                semantics,
11649                predicate,
11650                ParserSemanticHookRequest {
11651                    index,
11652                    rule_index,
11653                    pred_index,
11654                    context,
11655                    local_int_arg,
11656                    member_values,
11657                },
11658            );
11659        }
11660        let Some((_, _, predicate)) = predicates
11661            .iter()
11662            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
11663        else {
11664            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
11665                index,
11666                rule_index,
11667                pred_index,
11668                context,
11669                local_int_arg,
11670                member_values,
11671            }) {
11672                return result;
11673            }
11674            return self.unknown_predicate_result(rule_index, pred_index);
11675        };
11676        self.input.seek(index);
11677        match predicate {
11678            ParserPredicate::True => true,
11679            ParserPredicate::False => false,
11680            ParserPredicate::FalseWithMessage { .. } => false,
11681            ParserPredicate::Invoke { value } => {
11682                let key = (rule_index, pred_index);
11683                if !self.invoked_predicates.contains(&key) {
11684                    self.invoked_predicates.push(key);
11685                    use std::io::Write as _;
11686                    let mut stdout = std::io::stdout().lock();
11687                    let _ = writeln!(stdout, "eval={value}");
11688                }
11689                *value
11690            }
11691            ParserPredicate::LookaheadTextEquals { offset, text } => self
11692                .input
11693                .lt(*offset)
11694                .is_some_and(|token| Token::text(&token) == Some(*text)),
11695            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
11696                self.la(*offset) != *token_type
11697            }
11698            ParserPredicate::TokenPairAdjacent => {
11699                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
11700                    return false;
11701                };
11702                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
11703                    return false;
11704                };
11705                first + 1 == second
11706            }
11707            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
11708                .and_then(|context| {
11709                    context
11710                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
11711                        .next()
11712                        .map(crate::tree::RuleNodeView::text)
11713                })
11714                .is_none_or(|actual| actual != *text),
11715            ParserPredicate::LocalIntEquals { value } => {
11716                local_int_arg.is_none_or(|(_, actual)| actual == *value)
11717            }
11718            ParserPredicate::LocalIntLessOrEqual { value } => {
11719                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
11720            }
11721            ParserPredicate::MemberModuloEquals {
11722                member,
11723                modulus,
11724                value,
11725                equals,
11726            } => {
11727                if *modulus == 0 {
11728                    return false;
11729                }
11730                let actual = member_values.scalar(*member).unwrap_or_default() % *modulus;
11731                (actual == *value) == *equals
11732            }
11733            ParserPredicate::MemberEquals {
11734                member,
11735                value,
11736                equals,
11737            } => {
11738                let actual = member_values.scalar(*member).unwrap_or_default();
11739                (actual == *value) == *equals
11740            }
11741        }
11742    }
11743
11744    /// Returns a generated fail-option message for a predicate coordinate.
11745    fn parser_predicate_failure_message(
11746        &self,
11747        rule_index: usize,
11748        pred_index: usize,
11749        predicates: &[(usize, usize, ParserPredicate)],
11750    ) -> Option<&'static str> {
11751        predicates
11752            .iter()
11753            .find_map(|(rule, pred, predicate)| match predicate {
11754                ParserPredicate::FalseWithMessage { message }
11755                    if *rule == rule_index && *pred == pred_index =>
11756                {
11757                    Some(*message)
11758                }
11759                _ => None,
11760            })
11761    }
11762
11763    /// Returns a generated fail-option message for a `SemIR` predicate
11764    /// coordinate.
11765    pub fn parser_semantic_ir_predicate_failure_message(
11766        &self,
11767        rule_index: usize,
11768        pred_index: usize,
11769        semantics: &ParserSemantics,
11770    ) -> Option<&'static str> {
11771        semantics
11772            .predicates
11773            .iter()
11774            .find(|predicate| {
11775                predicate.rule_index == rule_index && predicate.pred_index == pred_index
11776            })
11777            .and_then(|predicate| predicate.failure_message)
11778    }
11779
11780    /// Returns the token-stream index after consuming `symbol` at `index`.
11781    ///
11782    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
11783    /// index stable and rely on `consumed_eof` to record that EOF was matched.
11784    /// The parser's stream cursor is left untouched: speculative recognition
11785    /// reads ahead by absolute index, so paying for `seek` on every visited
11786    /// state would dominate the hot path. Real consumption is committed by
11787    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
11788    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
11789        if symbol == TOKEN_EOF {
11790            return index;
11791        }
11792        self.input.next_visible_after(index)
11793    }
11794
11795    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
11796    /// decision that failed after consuming a shared prefix.
11797    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
11798        let text = display_input_text(&self.input.text(start_index, error_index));
11799        diagnostic_for_token(
11800            self.token_at(error_index).as_ref(),
11801            format!("no viable alternative at input '{text}'"),
11802        )
11803    }
11804
11805    /// Selects the diagnostic for a failed consuming transition after all
11806    /// recovery repairs have been ruled out.
11807    fn recovery_failure_diagnostic(
11808        &self,
11809        index: usize,
11810        decision_start_index: Option<usize>,
11811        expected_symbols: &BTreeSet<i32>,
11812    ) -> ParserDiagnostic {
11813        if expected_symbols.len() > 1 {
11814            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11815                return self.no_viable_alternative(decision_start, index);
11816            }
11817        }
11818        diagnostic_for_token(
11819            self.token_at(index).as_ref(),
11820            format!(
11821                "mismatched input {} expecting {}",
11822                self.token_at(index)
11823                    .as_ref()
11824                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11825                self.expected_symbols_display(expected_symbols)
11826            ),
11827        )
11828    }
11829
11830    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
11831    /// instead of inserting missing tokens in the caller.
11832    fn eof_rule_recovery_diagnostic(
11833        &self,
11834        index: usize,
11835        expected_symbols: &BTreeSet<i32>,
11836        expected: &ExpectedTokens,
11837    ) -> ParserDiagnostic {
11838        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
11839            &expected.symbols
11840        } else {
11841            expected_symbols
11842        };
11843        diagnostic_for_token(
11844            self.token_at(index).as_ref(),
11845            format!(
11846                "mismatched input {} expecting {}",
11847                self.token_at(index)
11848                    .as_ref()
11849                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11850                self.expected_symbols_display(symbols)
11851            ),
11852        )
11853    }
11854
11855    /// Returns token text for a buffered token interval used by generated
11856    /// `$text` actions.
11857    ///
11858    /// ANTLR treats EOF as a range boundary rather than printable input text,
11859    /// even when an action interval explicitly stops at the EOF token.
11860    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
11861        let Some(stop) = stop else {
11862            return String::new();
11863        };
11864        let stop = if self
11865            .token_at(stop)
11866            .is_some_and(|token| token.token_type() == TOKEN_EOF)
11867        {
11868            let Some(previous) = self.previous_token_index(stop) else {
11869                return String::new();
11870            };
11871            previous
11872        } else {
11873            stop
11874        };
11875        self.input.text(start, stop)
11876    }
11877
11878    /// Resets per-parse prediction diagnostics while keeping the parser-level
11879    /// reporting flag configured by generated harness code.
11880    fn clear_prediction_diagnostics(&mut self) {
11881        self.prediction_diagnostics.clear();
11882        self.reported_prediction_diagnostics.clear();
11883    }
11884
11885    /// Drops every per-parse cache that depends on ATN identity or pins
11886    /// recovery-symbol allocations.
11887    ///
11888    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
11889    /// same parser instance can legally be driven against different grammars
11890    /// in sequence. The four caches reset here are keyed by raw ATN
11891    /// coordinates (state numbers, rule indexes) and would silently hand back
11892    /// entries from a previous ATN if reused — pruning lookahead against the
11893    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
11894    /// for the rest of the process. Clearing them on every parse entry keeps
11895    /// the perf wins (caches still amortize within one parse) without making
11896    /// long-lived parsers leak memory or surface stale ATN data:
11897    ///
11898    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
11899    ///   functions of the ATN's state graph.
11900    /// * `state_expected_cache`, `state_expected_token_cache`,
11901    ///   `rule_stop_reach_cache`, and
11902    ///   `recovery_symbols_intern` together form
11903    ///   the identity invariant that lets `FastRecognizeKey` hash
11904    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
11905    ///   so a stale interned `Rc` cannot outlive its map entry.
11906    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
11907    ///   it is retained here and invalidated lazily when the ATN changes.
11908    fn reset_per_parse_caches(&mut self) {
11909        self.rule_first_set_cache.clear();
11910        self.decision_lookahead_cache.clear();
11911        self.ll1_decision_cache.clear();
11912        self.fast_predicate_cache.clear();
11913        self.rule_stop_reach_cache.clear();
11914        self.clean_memo_mode = CleanMemoMode::Probe;
11915        self.clean_memo_probe_seen.clear();
11916        self.clean_memo_probe_samples = 0;
11917        self.clean_memo_probe_repeats = 0;
11918        self.clean_memo_sparse_samples = 0;
11919        self.recovery_symbols_intern.clear();
11920        self.state_expected_cache.clear();
11921        self.state_expected_token_cache.clear();
11922    }
11923
11924    /// Buffers ANTLR-style diagnostic-listener messages for decision states
11925    /// where multiple clean alternatives survive full-context recognition.
11926    fn record_prediction_diagnostics(
11927        &mut self,
11928        atn: &Atn,
11929        state: AtnState<'_>,
11930        start_index: usize,
11931        outcomes: &[RecognizeOutcome],
11932    ) {
11933        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
11934            return;
11935        }
11936        let Some(decision) = atn
11937            .decision_to_state()
11938            .iter()
11939            .position(|state_number| state_number == state.state_number())
11940        else {
11941            return;
11942        };
11943        let Some(rule_index) = state.rule_index() else {
11944            return;
11945        };
11946        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
11947        for outcome in outcomes
11948            .iter()
11949            .filter(|outcome| outcome.diagnostics.is_empty())
11950        {
11951            let Some(alt) = outcome.decisions.first() else {
11952                continue;
11953            };
11954            alts_by_end
11955                .entry(outcome.index)
11956                .or_default()
11957                .insert(alt + 1);
11958        }
11959        let Some((&end_index, ambig_alts)) = alts_by_end
11960            .iter()
11961            .filter(|(_, alts)| alts.len() > 1)
11962            .max_by_key(|(end, _)| *end)
11963        else {
11964            return;
11965        };
11966        let rule_name = self
11967            .rule_names()
11968            .get(rule_index)
11969            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
11970        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
11971        let input = display_input_text(&self.input.text(start_index, stop_index));
11972        let alts = ambig_alts
11973            .iter()
11974            .map(usize::to_string)
11975            .collect::<Vec<_>>()
11976            .join(", ");
11977        let key = (decision, start_index, format!("{alts}:{input}"));
11978        if !self.reported_prediction_diagnostics.insert(key) {
11979            return;
11980        }
11981        let start_diagnostic = diagnostic_for_token(
11982            self.token_at(start_index),
11983            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
11984        );
11985        let stop_diagnostic = diagnostic_for_token(
11986            self.token_at(stop_index),
11987            format!(
11988                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
11989            ),
11990        );
11991        self.prediction_diagnostics.push(start_diagnostic);
11992        self.prediction_diagnostics.push(stop_diagnostic);
11993    }
11994
11995    /// Formats the tokens expected from an ATN state using ANTLR display names.
11996    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11997        expected_symbols_display(
11998            &state_expected_symbols(atn, state_number),
11999            self.vocabulary(),
12000        )
12001    }
12002
12003    /// Expected-token set at the parser's current ATN state — ANTLR's
12004    /// `getExpectedTokens()`. Generated recognizers expose this as
12005    /// `self.expected_tokens()` for embedded test actions
12006    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
12007    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
12008        let state = usize::try_from(self.data().state()).unwrap_or(0);
12009        ExpectedTokenSet {
12010            symbols: state_expected_symbols(atn, state),
12011        }
12012    }
12013
12014    /// Enables the bail error strategy: the first syntax error aborts the
12015    /// parse instead of recovering.
12016    pub const fn set_bail_on_error(&mut self, bail: bool) {
12017        self.bail_on_error = bail;
12018    }
12019
12020    /// Whether the bail error strategy is active.
12021    #[must_use]
12022    pub const fn bail_on_error(&self) -> bool {
12023        self.bail_on_error
12024    }
12025
12026    /// Names of the rules on the live invocation stack, current rule first —
12027    /// ANTLR's `getRuleInvocationStack()`.
12028    pub fn rule_invocation_stack(&self) -> Vec<String> {
12029        self.rule_context_stack
12030            .iter()
12031            .rev()
12032            .map(|frame| {
12033                self.data()
12034                    .rule_names()
12035                    .get(frame.rule_index)
12036                    .cloned()
12037                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
12038            })
12039            .collect()
12040    }
12041
12042    /// Invoking-state chain for the active rule context, current rule first.
12043    ///
12044    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
12045    pub fn active_invocation_states(&self) -> Vec<isize> {
12046        self.rule_context_stack
12047            .iter()
12048            .skip(1)
12049            .rev()
12050            .map(|frame| frame.invoking_state)
12051            .collect()
12052    }
12053
12054    /// Formats a buffered token in ANTLR's diagnostic token display form.
12055    pub fn token_display_at(&self, index: usize) -> Option<String> {
12056        self.token_at(index).map(|token| format!("{token}"))
12057    }
12058}
12059
12060impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
12061where
12062    S: TokenSource,
12063    H: SemanticHooks,
12064{
12065    fn parse_rule(
12066        &mut self,
12067        rule_index: usize,
12068        invoking_state: isize,
12069        precedence: i32,
12070    ) -> DirectAdaptiveParseResult<ParseTree> {
12071        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
12072            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
12073        )?;
12074        let stop_state = self
12075            .atn
12076            .rule_to_stop_state()
12077            .get(rule_index)
12078            .filter(|state| *state != usize::MAX)
12079            .ok_or(DirectAdaptiveParseControl::Fallback(
12080                DirectAdaptiveFallback::MissingAtn,
12081            ))?;
12082        let start_index = self.parser.current_visible_index();
12083        let mut context = ParserRuleContext::new(rule_index, invoking_state);
12084        if let Some(token) = self.parser.token_id_at(start_index) {
12085            self.parser.set_context_start(&mut context, token);
12086        }
12087        let mut state_number = start_state;
12088        let mut consumed_eof = false;
12089        while state_number != stop_state {
12090            self.step()?;
12091            let (transition, boundary) = self.next_transition(state_number, precedence)?;
12092            if boundary.is_some() {
12093                return Err(DirectAdaptiveParseControl::Fallback(
12094                    DirectAdaptiveFallback::LeftRecursiveBoundary,
12095                ));
12096            }
12097            match transition.data() {
12098                Transition::Epsilon { target } => {
12099                    state_number = target;
12100                }
12101                Transition::Precedence {
12102                    target,
12103                    precedence: transition_precedence,
12104                } => {
12105                    if transition_precedence < precedence {
12106                        return Err(DirectAdaptiveParseControl::Fallback(
12107                            DirectAdaptiveFallback::Precedence,
12108                        ));
12109                    }
12110                    state_number = target;
12111                }
12112                Transition::Rule {
12113                    rule_index,
12114                    follow_state,
12115                    precedence: rule_precedence,
12116                    ..
12117                } => {
12118                    let child = self.parse_rule(
12119                        rule_index,
12120                        invoking_state_number(state_number),
12121                        rule_precedence,
12122                    )?;
12123                    if self.parser.build_parse_trees {
12124                        self.parser.tree.add_child(&mut context, child);
12125                    }
12126                    state_number = follow_state;
12127                }
12128                Transition::Atom { .. }
12129                | Transition::Range { .. }
12130                | Transition::Set { .. }
12131                | Transition::NotSet { .. }
12132                | Transition::Wildcard { .. } => {
12133                    let (matched_eof, child) = self.consume_transition(transition)?;
12134                    consumed_eof |= matched_eof;
12135                    if let Some(child) = child {
12136                        self.parser.tree.add_child(&mut context, child);
12137                    }
12138                    state_number = transition.target();
12139                }
12140                Transition::Predicate { .. } => {
12141                    return Err(DirectAdaptiveParseControl::Fallback(
12142                        DirectAdaptiveFallback::Predicate,
12143                    ));
12144                }
12145                Transition::Action { .. } => {
12146                    return Err(DirectAdaptiveParseControl::Fallback(
12147                        DirectAdaptiveFallback::Action,
12148                    ));
12149                }
12150            }
12151        }
12152
12153        let stop_index = self
12154            .parser
12155            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
12156        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
12157            self.parser.set_context_stop(&mut context, token);
12158        }
12159        Ok(self.parser.rule_node(context))
12160    }
12161
12162    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
12163        self.steps += 1;
12164        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
12165            return Err(DirectAdaptiveParseControl::Fallback(
12166                DirectAdaptiveFallback::StepLimit,
12167            ));
12168        }
12169        Ok(())
12170    }
12171
12172    fn next_transition(
12173        &mut self,
12174        state_number: usize,
12175        precedence: i32,
12176    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
12177        let state = self
12178            .atn
12179            .state(state_number)
12180            .ok_or(DirectAdaptiveParseControl::Fallback(
12181                DirectAdaptiveFallback::MissingAtn,
12182            ))?;
12183        if state.is_rule_stop() {
12184            return Err(DirectAdaptiveParseControl::Fallback(
12185                DirectAdaptiveFallback::RuleStop,
12186            ));
12187        }
12188        let transition_index =
12189            self.transition_index(state_number, state.transitions().len(), precedence)?;
12190        let transition = state.transitions().get(transition_index).ok_or(
12191            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
12192        )?;
12193        let boundary = match &transition.data() {
12194            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
12195                left_recursive_boundary(self.atn, state, *target)
12196            }
12197            _ => None,
12198        };
12199        Ok((transition, boundary))
12200    }
12201
12202    fn transition_index(
12203        &mut self,
12204        state_number: usize,
12205        transition_count: usize,
12206        precedence: i32,
12207    ) -> DirectAdaptiveParseResult<usize> {
12208        match transition_count {
12209            0 => Err(DirectAdaptiveParseControl::Fallback(
12210                DirectAdaptiveFallback::NoTransition,
12211            )),
12212            1 => Ok(0),
12213            _ => {
12214                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
12215                    return Ok(alt);
12216                }
12217                let decision = self
12218                    .decision_by_state
12219                    .get(state_number)
12220                    .and_then(|decision| *decision)
12221                    .ok_or(DirectAdaptiveParseControl::Fallback(
12222                        DirectAdaptiveFallback::UnknownDecision,
12223                    ))?;
12224                let prediction = self
12225                    .simulator
12226                    .adaptive_predict_stream_info_with_precedence(
12227                        decision,
12228                        direct_precedence(precedence),
12229                        &mut self.parser.input,
12230                    )
12231                    .map_err(|_| {
12232                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
12233                    })?;
12234                if prediction.has_semantic_context {
12235                    return Err(DirectAdaptiveParseControl::Fallback(
12236                        DirectAdaptiveFallback::SemanticContext,
12237                    ));
12238                }
12239                prediction
12240                    .alt
12241                    .checked_sub(1)
12242                    .filter(|index| *index < transition_count)
12243                    .ok_or(DirectAdaptiveParseControl::Fallback(
12244                        DirectAdaptiveFallback::InvalidAlt,
12245                    ))
12246            }
12247        }
12248    }
12249
12250    fn ll1_transition_index(
12251        &mut self,
12252        state_number: usize,
12253        transition_count: usize,
12254    ) -> DirectAdaptiveParseResult<Option<usize>> {
12255        let state = self
12256            .atn
12257            .state(state_number)
12258            .ok_or(DirectAdaptiveParseControl::Fallback(
12259                DirectAdaptiveFallback::MissingAtn,
12260            ))?;
12261        if state.precedence_rule_decision() {
12262            return Ok(None);
12263        }
12264        let Some(rule_stop) = state
12265            .rule_index()
12266            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
12267        else {
12268            return Ok(None);
12269        };
12270        let symbol = self.parser.input.la_token(1);
12271        let entry = self
12272            .parser
12273            .cached_decision_lookahead(self.atn, state, rule_stop);
12274        Ok(
12275            ll1_greedy_alt(&entry, symbol, state.non_greedy())
12276                .filter(|alt| *alt < transition_count),
12277        )
12278    }
12279
12280    fn consume_transition(
12281        &mut self,
12282        transition: ParserTransition<'_>,
12283    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
12284        let symbol = self.parser.input.la_token(1);
12285        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
12286            return Err(DirectAdaptiveParseControl::Fallback(
12287                DirectAdaptiveFallback::TokenMismatch,
12288            ));
12289        }
12290        let token = self
12291            .parser
12292            .input
12293            .lt_id(1)
12294            .ok_or(DirectAdaptiveParseControl::Fallback(
12295                DirectAdaptiveFallback::TokenMismatch,
12296            ))?;
12297        let matched_eof = symbol == TOKEN_EOF;
12298        if !matched_eof {
12299            self.parser.consume();
12300        }
12301        let child = self
12302            .parser
12303            .build_parse_trees
12304            .then(|| self.parser.terminal_tree(token));
12305        Ok((matched_eof, child))
12306    }
12307}
12308
12309/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
12310/// transformed left-recursive rule.
12311fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
12312    if !state.precedence_rule_decision() {
12313        return None;
12314    }
12315    let target_state = atn.state(target)?;
12316    if target_state.kind() == AtnStateKind::LoopEnd {
12317        return None;
12318    }
12319    state.rule_index()
12320}
12321
12322/// Selects the first outer alternative observed for a rule path.
12323///
12324/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
12325/// outer decision. The metadata recognizer only needs this when a generated
12326/// grammar opts into that target template; otherwise the value remains `0` and
12327/// parse-tree rendering is unchanged.
12328fn next_alt_number(
12329    state: AtnState<'_>,
12330    transition_count: usize,
12331    transition_index: usize,
12332    current_alt_number: usize,
12333    track_alt_numbers: bool,
12334) -> usize {
12335    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
12336        return current_alt_number;
12337    }
12338    if matches!(
12339        state.kind(),
12340        AtnStateKind::Basic
12341            | AtnStateKind::BlockStart
12342            | AtnStateKind::PlusBlockStart
12343            | AtnStateKind::StarBlockStart
12344            | AtnStateKind::StarLoopEntry
12345    ) && !state.precedence_rule_decision()
12346    {
12347        return transition_index + 1;
12348    }
12349    current_alt_number
12350}
12351
12352/// Converts an ATN state number into the signed invoking-state slot used by
12353/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
12354fn invoking_state_number(state_number: usize) -> isize {
12355    isize::try_from(state_number).unwrap_or(isize::MAX)
12356}
12357
12358const fn packed_i32(value: u32) -> i32 {
12359    i32::from_le_bytes(value.to_le_bytes())
12360}
12361
12362fn direct_precedence(precedence: i32) -> usize {
12363    usize::try_from(precedence.max(0)).unwrap_or_default()
12364}
12365
12366fn token_input_display(token: &impl Token) -> String {
12367    format!("'{}'", token.text().unwrap_or("<EOF>"))
12368}
12369
12370fn display_input_text(text: &str) -> String {
12371    let mut out = String::new();
12372    for ch in text.chars() {
12373        match ch {
12374            '\n' => out.push_str("\\n"),
12375            '\r' => out.push_str("\\r"),
12376            '\t' => out.push_str("\\t"),
12377            other => out.push(other),
12378        }
12379    }
12380    out
12381}
12382
12383fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
12384    let (line, column, offending) = token.map_or((0, 0, None), |token| {
12385        (token.line(), token.column(), Some(token.token_id()))
12386    });
12387    ParserDiagnostic {
12388        line,
12389        column,
12390        message,
12391        offending,
12392    }
12393}
12394
12395fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
12396    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
12397}
12398
12399fn expected_symbols_display_iter(
12400    symbols: impl IntoIterator<Item = i32>,
12401    vocabulary: &Vocabulary,
12402) -> String {
12403    let items = symbols
12404        .into_iter()
12405        .map(|symbol| expected_symbol_display(symbol, vocabulary))
12406        .collect::<Vec<_>>();
12407    if let [single] = items.as_slice() {
12408        return single.clone();
12409    }
12410    format!("{{{}}}", items.join(", "))
12411}
12412
12413fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
12414    if symbol == TOKEN_EOF {
12415        return "<EOF>".to_owned();
12416    }
12417    vocabulary.display_name(symbol)
12418}
12419
12420fn caller_follow_token_info_for_stream<S: TokenSource>(
12421    input: &mut CommonTokenStream<S>,
12422    index: usize,
12423) -> (i32, bool, bool) {
12424    // Generated callers own statement separators; leave them available when
12425    // an interpreted child rule can either stop before or consume one.
12426    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
12427        input.fill();
12428    }
12429    let token_type = input.token_type_at_index(index);
12430    let visible_channel = input.channel();
12431    let token = input.get(index);
12432    let is_boundary = token
12433        .as_ref()
12434        .and_then(Token::text)
12435        .is_some_and(is_caller_follow_boundary_text);
12436    let is_boundary_gap = token.as_ref().is_some_and(|token| {
12437        token.channel() != visible_channel
12438            || is_caller_follow_boundary_gap_text(token.text_or_empty())
12439    });
12440    (token_type, is_boundary, is_boundary_gap)
12441}
12442
12443fn is_caller_follow_boundary_text(text: &str) -> bool {
12444    text.chars().any(|ch| ch == ';' || ch == '\n')
12445        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
12446}
12447
12448fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
12449    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
12450}
12451
12452/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
12453/// Inline insertion in those precedence loops can synthesize a missing operand
12454/// before an operator and then block the legitimate loop-exit path.
12455fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
12456    let Some(rule_index) = state.rule_index() else {
12457        return false;
12458    };
12459    atn.rule_to_start_state()
12460        .get(rule_index)
12461        .and_then(|state_number| atn.state(state_number))
12462        .is_some_and(AtnState::left_recursive_rule)
12463}
12464
12465/// Picks the better of two `parse_atn_rule` passes (with and without the
12466/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
12467/// recovered one; among recovered outcomes the second pass is preferred
12468/// because the no-prefilter walk reaches ANTLR-style recovery inside child
12469/// rules. If both passes failed, the second pass's expected-token snapshot
12470/// is returned so the caller renders the same diagnostic ANTLR would.
12471fn select_better_top_outcome(
12472    first: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
12473    second: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
12474    arena: &RecognitionArena,
12475) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
12476    match (first, second) {
12477        (Ok(first), Ok(second)) => {
12478            if arena.diagnostics(first.0.diagnostics).next().is_none() {
12479                Ok(first)
12480            } else {
12481                Ok(second)
12482            }
12483        }
12484        (Ok(first), Err(_)) => Ok(first),
12485        (Err(_), Ok(second)) => Ok(second),
12486        (Err(_), Err(second_expected)) => Err(second_expected),
12487    }
12488}
12489
12490/// Chooses the outermost parse result that consumed the most input.
12491///
12492/// The recognizer intentionally keeps shorter endpoints available while walking
12493/// nested rule transitions so callers can satisfy following tokens such as
12494/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
12495fn select_best_fast_outcome(
12496    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
12497    prediction_mode: PredictionMode,
12498    caller_follow: Option<&TokenBitSet>,
12499    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
12500    arena: &RecognitionArena,
12501) -> Option<FastRecognizeOutcome> {
12502    let mut best = None;
12503    let mut best_caller_follow = None;
12504    for outcome in outcomes {
12505        if matches!(
12506            prediction_mode,
12507            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
12508        ) && outcome.diagnostics.is_empty()
12509            && let Some(follow) = caller_follow
12510        {
12511            let (token_type, is_boundary, _) = token_info_at(outcome.index);
12512            if is_boundary && follow.contains(token_type) {
12513                let replace =
12514                    best_caller_follow
12515                        .as_ref()
12516                        .is_none_or(|existing: &FastRecognizeOutcome| {
12517                            (outcome.index, outcome.consumed_eof)
12518                                < (existing.index, existing.consumed_eof)
12519                        });
12520                if replace {
12521                    best_caller_follow = Some(outcome);
12522                }
12523            }
12524        }
12525        let Some(existing) = best else {
12526            best = Some(outcome);
12527            continue;
12528        };
12529        let outcome_position = (outcome.index, outcome.consumed_eof);
12530        let best_position = (existing.index, existing.consumed_eof);
12531        let better = match prediction_mode {
12532            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
12533                outcome_position,
12534                outcome.diagnostics,
12535                best_position,
12536                existing.diagnostics,
12537                arena,
12538            ),
12539            PredictionMode::Sll => outcome.index > existing.index,
12540        };
12541        best = Some(if better { outcome } else { existing });
12542    }
12543    let should_use_caller_follow =
12544        best_caller_follow
12545            .as_ref()
12546            .zip(best.as_ref())
12547            .is_some_and(|(candidate, selected)| {
12548                if !selected.diagnostics.is_empty() {
12549                    return true;
12550                }
12551                candidate.index < selected.index
12552                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
12553            });
12554    if should_use_caller_follow {
12555        best_caller_follow
12556    } else {
12557        best
12558    }
12559}
12560
12561fn select_best_outcome(
12562    outcomes: impl Iterator<Item = RecognizeOutcome>,
12563    prediction_mode: PredictionMode,
12564    arena: &RecognitionArena,
12565) -> Option<RecognizeOutcome> {
12566    let outcomes = outcomes.collect::<Vec<_>>();
12567    let prefer_first_tie = outcomes
12568        .iter()
12569        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
12570    outcomes.into_iter().reduce(|best, outcome| {
12571        let outcome_position = (outcome.index, outcome.consumed_eof);
12572        let best_position = (best.index, best.consumed_eof);
12573        let better = match prediction_mode {
12574            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
12575                outcome_is_better(
12576                    outcome_position,
12577                    outcome.diagnostics,
12578                    best_position,
12579                    best.diagnostics,
12580                    arena,
12581                ) || (outcome_position == best_position
12582                    && arena.diagnostics_len(outcome.diagnostics)
12583                        == arena.diagnostics_len(best.diagnostics)
12584                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
12585                        == arena.diagnostics_recovery_rank(best.diagnostics)
12586                    && (outcome.decisions < best.decisions
12587                        || (!prefer_first_tie
12588                            && outcome.decisions == best.decisions
12589                            && outcome.actions > best.actions)))
12590            }
12591            PredictionMode::Sll => {
12592                outcome_position > best_position
12593                    || (outcome_position == best_position
12594                        && !prefer_first_tie
12595                        && (outcome.decisions < best.decisions
12596                            || (outcome.decisions == best.decisions
12597                                && outcome_is_better(
12598                                    outcome_position,
12599                                    outcome.diagnostics,
12600                                    best_position,
12601                                    best.diagnostics,
12602                                    arena,
12603                                ))))
12604            }
12605        };
12606        if better {
12607            return outcome;
12608        }
12609        best
12610    })
12611}
12612
12613/// Records the serialized transition order at parser decision states.
12614///
12615/// When two clean paths consume the same input, ANTLR's adaptive prediction
12616/// chooses by alternative order. Keeping this compact trace lets the metadata
12617/// recognizer distinguish greedy and non-greedy optional blocks without a full
12618/// prediction simulator.
12619fn transition_decision(
12620    atn: &Atn,
12621    state: AtnState<'_>,
12622    transition_count: usize,
12623    transition_index: usize,
12624    predicates: &[(usize, usize, ParserPredicate)],
12625) -> Option<usize> {
12626    if transition_count <= 1 || decision_reaches_unsupported_predicate(atn, state, predicates) {
12627        return None;
12628    }
12629    Some(transition_index)
12630}
12631
12632/// Reports whether a state should reset the active no-viable decision start.
12633///
12634/// Loop entry/back states are continuations of the surrounding adaptive
12635/// prediction; resetting at those states would turn LL-star failures back into
12636/// ordinary mismatches.
12637fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
12638    transition_count > 1
12639        && !matches!(
12640            state.kind(),
12641            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
12642        )
12643}
12644
12645/// Marks a farthest expected-token set as no-viable when multiple alternatives
12646/// failed after the active decision had already consumed input.
12647fn record_no_viable_if_ambiguous(
12648    expected: &mut ExpectedTokens,
12649    decision_start_index: Option<usize>,
12650    index: usize,
12651) {
12652    if expected.index == Some(index) && expected.symbols.len() > 1 {
12653        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
12654            expected.record_no_viable(decision_start, index);
12655        }
12656    }
12657}
12658
12659/// Records a no-viable decision caused by a failed semantic predicate before
12660/// any consuming transition can contribute an expected-token set.
12661const fn record_predicate_no_viable(
12662    expected: &mut ExpectedTokens,
12663    decision_start_index: Option<usize>,
12664    index: usize,
12665) {
12666    if let Some(decision_start) = decision_start_index {
12667        expected.record_no_viable(decision_start, index);
12668    }
12669}
12670
12671/// Returns the active decision start only when the error is past that start.
12672const fn no_viable_decision_start(
12673    decision_start_index: Option<usize>,
12674    index: usize,
12675) -> Option<usize> {
12676    match decision_start_index {
12677        Some(start) if index > start => Some(start),
12678        _ => None,
12679    }
12680}
12681
12682/// Restores expected-token bookkeeping when a child rule found a clean
12683/// consuming path; failures in longer child alternatives should not pollute the
12684/// caller's final expectation set.
12685fn restore_expected(
12686    children: &[RecognizeOutcome],
12687    child_start_index: usize,
12688    expected: &mut ExpectedTokens,
12689    snapshot: ExpectedTokens,
12690    preserve_child_expected: bool,
12691) {
12692    if preserve_child_expected {
12693        return;
12694    }
12695    if children
12696        .iter()
12697        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
12698    {
12699        *expected = snapshot;
12700    }
12701}
12702
12703/// Reports whether a decision can reach a predicate the generator did not
12704/// translate. Static alternative order is unsafe for those context predicates.
12705fn decision_reaches_unsupported_predicate(
12706    atn: &Atn,
12707    state: AtnState<'_>,
12708    predicates: &[(usize, usize, ParserPredicate)],
12709) -> bool {
12710    state.transitions().iter().any(|transition| {
12711        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
12712    })
12713}
12714
12715/// Walks epsilon-like edges from one transition to find unsupported predicates.
12716fn transition_reaches_unsupported_predicate(
12717    atn: &Atn,
12718    transition: ParserTransition<'_>,
12719    predicates: &[(usize, usize, ParserPredicate)],
12720    visited: &mut BTreeSet<usize>,
12721) -> bool {
12722    match &transition.data() {
12723        Transition::Predicate {
12724            rule_index,
12725            pred_index,
12726            ..
12727        } => !predicates
12728            .iter()
12729            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
12730        Transition::Epsilon { target }
12731        | Transition::Action { target, .. }
12732        | Transition::Rule { target, .. } => {
12733            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
12734        }
12735        Transition::Precedence { .. }
12736        | Transition::Atom { .. }
12737        | Transition::Range { .. }
12738        | Transition::Set { .. }
12739        | Transition::NotSet { .. }
12740        | Transition::Wildcard { .. } => false,
12741    }
12742}
12743
12744/// Finds an unsupported predicate reachable before a consuming transition.
12745fn state_reaches_unsupported_predicate(
12746    atn: &Atn,
12747    state_number: usize,
12748    predicates: &[(usize, usize, ParserPredicate)],
12749    visited: &mut BTreeSet<usize>,
12750) -> bool {
12751    if !visited.insert(state_number) {
12752        return false;
12753    }
12754    let Some(state) = atn.state(state_number) else {
12755        return false;
12756    };
12757    state.transitions().iter().any(|transition| {
12758        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
12759    })
12760}
12761
12762/// Adds a decision step to the front of an already-recognized suffix path.
12763fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
12764    if let Some(decision) = decision {
12765        outcome.decisions.insert(0, decision);
12766    }
12767}
12768
12769fn outcome_is_better(
12770    outcome_position: (usize, bool),
12771    outcome_diagnostics: DiagnosticSeqId,
12772    best_position: (usize, bool),
12773    best_diagnostics: DiagnosticSeqId,
12774    arena: &RecognitionArena,
12775) -> bool {
12776    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
12777    let best_len = arena.diagnostics_len(best_diagnostics);
12778    outcome_position > best_position
12779        || (outcome_position == best_position
12780            && (outcome_len < best_len
12781                || (outcome_len == best_len
12782                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
12783                        < arena.diagnostics_recovery_rank(best_diagnostics))))
12784}
12785
12786fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
12787    if outcomes
12788        .iter()
12789        .any(|outcome| outcome.diagnostics.is_empty())
12790    {
12791        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12792    }
12793}
12794
12795fn discard_recovered_outcomes_if_clean_path_exists(
12796    outcomes: &mut Vec<RecognizeOutcome>,
12797    arena: &RecognitionArena,
12798) {
12799    if outcomes
12800        .iter()
12801        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
12802    {
12803        return;
12804    }
12805    if outcomes
12806        .iter()
12807        .any(|outcome| outcome.diagnostics.is_empty())
12808    {
12809        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12810    }
12811}
12812
12813/// Reports whether a recovered outcome came from an explicit predicate
12814/// fail-option and therefore should compete with shorter clean loop exits.
12815fn outcome_has_rule_failure_diagnostic(
12816    outcome: &RecognizeOutcome,
12817    arena: &RecognitionArena,
12818) -> bool {
12819    arena
12820        .diagnostics(outcome.diagnostics)
12821        .any(|diagnostic| diagnostic.message.starts_with("rule "))
12822}
12823
12824/// Removes equivalent endpoints before memoizing a state result while
12825/// preserving ATN transition-discovery order.
12826///
12827/// Outcomes are compared on observable recognition state — the input index,
12828/// EOF consumption, and diagnostics — without descending into the parse-tree
12829/// fragment carried by `nodes`. Two paths reaching the same point with
12830/// different node trees would otherwise prevent memoization from collapsing
12831/// equivalent suffixes and explode the speculative-path cache.
12832///
12833/// The first occurrence per recognition key wins, which matches ANTLR's
12834/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
12835/// transitions before loop-exit, so the first-discovered outcome carries the
12836/// greedy parse-tree fragment.
12837fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
12838    if outcomes.len() < 2 {
12839        return;
12840    }
12841    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
12842    outcomes.retain(|outcome| {
12843        seen.insert((
12844            outcome.index,
12845            outcome.consumed_eof,
12846            arena.diagnostics_len(outcome.diagnostics),
12847            arena.diagnostics_recovery_rank(outcome.diagnostics),
12848        ))
12849    });
12850}
12851
12852const FAST_OUTCOME_INLINE_KEYS: usize = 8;
12853const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
12854const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
12855const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
12856
12857#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12858enum FastOutcomeDedupStrategy {
12859    Inline,
12860    Dense,
12861    Sparse,
12862}
12863
12864impl FastOutcomeDedupScratch {
12865    fn prepare_dense(&mut self, word_count: usize) {
12866        while let Some(word_index) = self.touched_dense_words.pop() {
12867            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
12868        }
12869        if self.dense_words.len() < word_count {
12870            self.dense_words.resize(word_count, 0);
12871        }
12872    }
12873}
12874
12875fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
12876    let first_index = outcomes.first()?.index;
12877    let (min_index, max_index) = outcomes[1..].iter().fold(
12878        (first_index, first_index),
12879        |(min_index, max_index), outcome| {
12880            (min_index.min(outcome.index), max_index.max(outcome.index))
12881        },
12882    );
12883    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
12884    let bit_count = index_span.checked_mul(2)?;
12885    let word_count =
12886        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
12887    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
12888    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
12889    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
12890        .then_some((min_index, word_count))
12891}
12892
12893#[cfg(feature = "perf-counters")]
12894fn record_clean_fast_outcome_dedup(
12895    strategy: FastOutcomeDedupStrategy,
12896    input_len: usize,
12897    output_len: usize,
12898    dense_words: usize,
12899) {
12900    let counter = match strategy {
12901        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
12902        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
12903        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
12904    };
12905    perf_counters::inc(
12906        &perf_counters::OUTCOME_DEDUPE_INPUTS,
12907        u64::try_from(input_len).unwrap_or(u64::MAX),
12908    );
12909    perf_counters::inc(
12910        &perf_counters::OUTCOME_DEDUPE_REMOVED,
12911        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
12912    );
12913    perf_counters::inc(counter, 1);
12914    perf_counters::inc(
12915        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
12916        u64::try_from(dense_words).unwrap_or(u64::MAX),
12917    );
12918}
12919
12920/// Removes duplicate clean endpoints while preserving transition-discovery
12921/// order. Tiny lists stay on the stack; larger compact ranges use a direct
12922/// bitmap, and only wide sparse ranges pay for hashing.
12923fn dedupe_clean_fast_outcomes(
12924    outcomes: &mut Vec<FastRecognizeOutcome>,
12925    scratch: &mut FastOutcomeDedupScratch,
12926) -> FastOutcomeDedupStrategy {
12927    #[cfg(feature = "perf-counters")]
12928    let input_len = outcomes.len();
12929    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
12930        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
12931        let mut inline_len = 0_usize;
12932        outcomes.retain(|outcome| {
12933            let key = (outcome.index, outcome.consumed_eof);
12934            if inline_keys[..inline_len].contains(&key) {
12935                return false;
12936            }
12937            inline_keys[inline_len] = key;
12938            inline_len += 1;
12939            true
12940        });
12941        #[cfg(feature = "perf-counters")]
12942        record_clean_fast_outcome_dedup(
12943            FastOutcomeDedupStrategy::Inline,
12944            input_len,
12945            outcomes.len(),
12946            0,
12947        );
12948        return FastOutcomeDedupStrategy::Inline;
12949    }
12950
12951    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
12952        scratch.prepare_dense(word_count);
12953        outcomes.retain(|outcome| {
12954            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
12955            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
12956            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
12957            let word = &mut scratch.dense_words[word_index];
12958            if *word & bit != 0 {
12959                return false;
12960            }
12961            if *word == 0 {
12962                scratch
12963                    .touched_dense_words
12964                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
12965            }
12966            *word |= bit;
12967            true
12968        });
12969        #[cfg(feature = "perf-counters")]
12970        record_clean_fast_outcome_dedup(
12971            FastOutcomeDedupStrategy::Dense,
12972            input_len,
12973            outcomes.len(),
12974            word_count,
12975        );
12976        return FastOutcomeDedupStrategy::Dense;
12977    }
12978
12979    scratch.sparse_keys.clear();
12980    scratch.sparse_keys.reserve(outcomes.len());
12981    outcomes.retain(|outcome| {
12982        scratch
12983            .sparse_keys
12984            .insert((outcome.index, outcome.consumed_eof))
12985    });
12986    #[cfg(feature = "perf-counters")]
12987    record_clean_fast_outcome_dedup(
12988        FastOutcomeDedupStrategy::Sparse,
12989        input_len,
12990        outcomes.len(),
12991        0,
12992    );
12993    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
12994        scratch.sparse_keys = FxHashSet::default();
12995    }
12996    FastOutcomeDedupStrategy::Sparse
12997}
12998
12999/// Sorts and removes equivalent endpoints, including action traces and the
13000/// arena-backed node sequence's structural contents.
13001fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
13002    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
13003    outcomes
13004        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
13005}
13006
13007fn compare_recognize_outcomes(
13008    left: &RecognizeOutcome,
13009    right: &RecognizeOutcome,
13010    arena: &RecognitionArena,
13011) -> Ordering {
13012    left.index
13013        .cmp(&right.index)
13014        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
13015        .then_with(|| left.alt_number.cmp(&right.alt_number))
13016        .then_with(|| left.member_values.cmp(&right.member_values))
13017        .then_with(|| left.return_values.cmp(&right.return_values))
13018        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
13019        .then_with(|| left.decisions.cmp(&right.decisions))
13020        .then_with(|| left.actions.cmp(&right.actions))
13021        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
13022}
13023
13024impl<S, H> Recognizer for BaseParser<S, H>
13025where
13026    S: TokenSource,
13027    H: SemanticHooks,
13028{
13029    fn data(&self) -> &RecognizerData {
13030        &self.data
13031    }
13032
13033    fn data_mut(&mut self) -> &mut RecognizerData {
13034        &mut self.data
13035    }
13036}
13037
13038impl<S, H> Parser for BaseParser<S, H>
13039where
13040    S: TokenSource,
13041    H: SemanticHooks,
13042{
13043    fn build_parse_trees(&self) -> bool {
13044        self.build_parse_trees
13045    }
13046
13047    fn set_build_parse_trees(&mut self, build: bool) {
13048        self.build_parse_trees = build;
13049    }
13050
13051    fn number_of_syntax_errors(&self) -> usize {
13052        Self::number_of_syntax_errors(self)
13053    }
13054
13055    fn report_diagnostic_errors(&self) -> bool {
13056        self.report_diagnostic_errors
13057    }
13058
13059    fn set_report_diagnostic_errors(&mut self, report: bool) {
13060        self.report_diagnostic_errors = report;
13061    }
13062
13063    fn prediction_mode(&self) -> PredictionMode {
13064        self.prediction_mode
13065    }
13066
13067    fn set_prediction_mode(&mut self, mode: PredictionMode) {
13068        self.prediction_mode = mode;
13069    }
13070
13071    fn max_rule_depth(&self) -> Option<usize> {
13072        self.max_rule_depth
13073    }
13074
13075    fn set_max_rule_depth(&mut self, depth: Option<usize>) {
13076        self.max_rule_depth = depth;
13077    }
13078
13079    fn add_parse_listener(&mut self, listener: Box<dyn ParseListener>) {
13080        self.parse_listeners.push(ParseListenerSlot(listener));
13081    }
13082
13083    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
13084        Self::remove_parse_listeners(self)
13085    }
13086}
13087
13088#[cfg(test)]
13089#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
13090mod tests {
13091    use super::*;
13092    use crate::atn::parser::{
13093        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
13094    };
13095    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
13096    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
13097    use crate::token_stream::CommonTokenStream;
13098    use crate::tree::{NodeKind, ParseTreeStats};
13099    use crate::vocabulary::Vocabulary;
13100    use std::cell::RefCell;
13101    use std::mem::size_of;
13102    use std::rc::Rc;
13103    use std::sync::{Arc, Mutex};
13104
13105    #[test]
13106    fn fx_hasher_write_matches_typed_methods_for_full_words() {
13107        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
13108        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
13109        // fields) must hash the same as the typed methods, otherwise an
13110        // `FxHashMap` keyed on such a type silently disagrees with itself
13111        // depending on which entry point the caller used. Verify the
13112        // little-endian word equivalence this PR established.
13113        let value: u64 = 0x0102_0304_0506_0708;
13114        let mut typed = FxHasher::default();
13115        typed.write_u64(value);
13116        let mut bytewise = FxHasher::default();
13117        bytewise.write(&value.to_le_bytes());
13118        assert_eq!(typed.finish(), bytewise.finish());
13119    }
13120
13121    #[derive(Clone, Debug)]
13122    struct TestToken {
13123        spec: TokenSpec,
13124        id: TokenId,
13125        source_name: String,
13126    }
13127
13128    impl TestToken {
13129        fn new(token_type: i32) -> Self {
13130            Self {
13131                spec: TokenSpec::explicit(token_type, ""),
13132                id: TokenId::try_from(0).expect("zero token ID"),
13133                source_name: String::new(),
13134            }
13135        }
13136
13137        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
13138            Self {
13139                spec: TokenSpec::eof(index, index, line, column),
13140                id: TokenId::try_from(0).expect("zero token ID"),
13141                source_name: source_name.to_owned(),
13142            }
13143        }
13144
13145        fn with_text(mut self, text: impl Into<String>) -> Self {
13146            self.spec.text = Some(text.into());
13147            self
13148        }
13149
13150        const fn with_channel(mut self, channel: i32) -> Self {
13151            self.spec.channel = channel;
13152            self
13153        }
13154
13155        fn with_span(mut self, start: usize, stop: usize) -> Self {
13156            self.spec = self.spec.with_span(start, stop);
13157            self
13158        }
13159
13160        fn with_byte_span(mut self, start: usize, stop: usize) -> Self {
13161            self.spec = self.spec.with_byte_span(start, stop);
13162            self
13163        }
13164
13165        const fn with_position(mut self, line: usize, column: usize) -> Self {
13166            self.spec.line = line;
13167            self.spec.column = column;
13168            self
13169        }
13170
13171        fn set_token_index(&mut self, index: isize) {
13172            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
13173        }
13174    }
13175
13176    impl Token for TestToken {
13177        fn token_id(&self) -> TokenId {
13178            self.id
13179        }
13180
13181        fn token_type(&self) -> i32 {
13182            self.spec.token_type
13183        }
13184
13185        fn channel(&self) -> i32 {
13186            self.spec.channel
13187        }
13188
13189        fn start(&self) -> usize {
13190            self.spec.start
13191        }
13192
13193        fn stop(&self) -> usize {
13194            self.spec.stop
13195        }
13196
13197        fn line(&self) -> usize {
13198            self.spec.line
13199        }
13200
13201        fn column(&self) -> usize {
13202            self.spec.column
13203        }
13204
13205        fn text(&self) -> Option<&str> {
13206            self.spec.text.as_deref()
13207        }
13208
13209        fn source_name(&self) -> &str {
13210            &self.source_name
13211        }
13212
13213        fn start_byte(&self) -> Option<usize> {
13214            (self.spec.start_byte != usize::MAX).then_some(self.spec.start_byte)
13215        }
13216
13217        fn stop_byte(&self) -> Option<usize> {
13218            (self.spec.stop_byte != usize::MAX).then_some(self.spec.stop_byte)
13219        }
13220    }
13221
13222    #[derive(Debug)]
13223    struct Source {
13224        tokens: Vec<TestToken>,
13225        index: usize,
13226    }
13227
13228    impl TokenSource for Source {
13229        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
13230            let token = self
13231                .tokens
13232                .get(self.index)
13233                .cloned()
13234                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
13235            self.index += 1;
13236            sink.push(token.spec)
13237        }
13238
13239        fn line(&self) -> usize {
13240            1
13241        }
13242
13243        fn column(&self) -> usize {
13244            self.index
13245        }
13246
13247        fn source_name(&self) -> &'static str {
13248            "parser-test"
13249        }
13250    }
13251
13252    #[derive(Clone, Debug, Eq, PartialEq)]
13253    struct RecordedDiagnostic {
13254        grammar_file_name: String,
13255        offending_text: Option<String>,
13256        line: usize,
13257        column: usize,
13258        span: Option<std::ops::Range<usize>>,
13259        message: String,
13260        error: Option<AntlrError>,
13261    }
13262
13263    #[derive(Clone, Debug)]
13264    struct RecordingErrorListener {
13265        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
13266    }
13267
13268    impl<R> crate::ErrorListener<R> for RecordingErrorListener
13269    where
13270        R: Recognizer + ?Sized,
13271    {
13272        fn syntax_error(&mut self, recognizer: &R, event: &SyntaxErrorEvent<'_>) {
13273            self.diagnostics
13274                .lock()
13275                .expect("recorded diagnostics lock")
13276                .push(RecordedDiagnostic {
13277                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
13278                    offending_text: event
13279                        .offending
13280                        .and_then(|token| token.text().map(str::to_owned)),
13281                    line: event.line,
13282                    column: event.column,
13283                    span: event.span.clone(),
13284                    message: event.message.to_owned(),
13285                    error: event.error.cloned(),
13286                });
13287        }
13288    }
13289
13290    #[derive(Debug)]
13291    struct ReportingSource {
13292        source: Source,
13293        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
13294    }
13295
13296    impl TokenSource for ReportingSource {
13297        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
13298            self.source.next_token(sink)
13299        }
13300
13301        fn line(&self) -> usize {
13302            self.source.line()
13303        }
13304
13305        fn column(&self) -> usize {
13306            self.source.column()
13307        }
13308
13309        fn source_name(&self) -> &str {
13310            self.source.source_name()
13311        }
13312
13313        fn report_error(&self, error: &TokenSourceError) -> bool {
13314            self.diagnostics.borrow_mut().push(error.clone());
13315            true
13316        }
13317    }
13318
13319    fn mini_parser_data() -> RecognizerData {
13320        RecognizerData::new(
13321            "Mini.g4",
13322            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
13323        )
13324        .with_rule_names(["s"])
13325    }
13326
13327    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
13328        let data = mini_parser_data();
13329        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
13330    }
13331
13332    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
13333    where
13334        H: SemanticHooks,
13335    {
13336        BaseParser::with_semantic_hooks(
13337            CommonTokenStream::new(Source { tokens, index: 0 }),
13338            mini_parser_data(),
13339            hooks,
13340        )
13341    }
13342
13343    #[test]
13344    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
13345        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
13346        parser.remove_error_listeners();
13347        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13348        parser.add_error_listener(RecordingErrorListener {
13349            diagnostics: Arc::clone(&diagnostics),
13350        });
13351        let parser_diagnostics = [ParserDiagnostic {
13352            line: 1,
13353            column: 2,
13354            message: "missing 'x' at 'y'".to_owned(),
13355            offending: None,
13356        }];
13357        let token_errors = [
13358            TokenSourceError::new(1, 1, "token recognition error at: '@'").with_span(1..2),
13359            TokenSourceError::new(1, 3, "token recognition error at: '#'").with_span(3..4),
13360        ];
13361
13362        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
13363
13364        // The interleaved token/parser diagnostic stream (ordering, columns, messages) is one
13365        // reviewable snapshot instead of three hand-written RecordedDiagnostic literals.
13366        insta::assert_debug_snapshot!(
13367            "parser_dispatches_recovery_diagnostics_through_registered_listeners",
13368            *diagnostics.lock().expect("recorded diagnostics lock")
13369        );
13370
13371        parser.remove_error_listeners();
13372        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
13373        assert_eq!(
13374            diagnostics.lock().expect("recorded diagnostics lock").len(),
13375            3
13376        );
13377    }
13378
13379    #[test]
13380    fn recovery_diagnostics_expose_the_offending_token_to_listeners() {
13381        let mut parser = mini_parser(vec![
13382            TestToken::new(7)
13383                .with_text("oops")
13384                .with_span(0, 3)
13385                .with_byte_span(0, 4)
13386                .with_position(1, 2),
13387            TestToken::eof("parser-test", 4, 1, 6),
13388        ]);
13389        parser.remove_error_listeners();
13390        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13391        parser.add_error_listener(RecordingErrorListener {
13392            diagnostics: Arc::clone(&diagnostics),
13393        });
13394        let offending = parser.input.lt_id(1);
13395        assert!(offending.is_some(), "current token should be buffered");
13396        let parser_diagnostics = [ParserDiagnostic {
13397            line: 1,
13398            column: 2,
13399            message: "extraneous input 'oops'".to_owned(),
13400            offending,
13401        }];
13402
13403        parser.dispatch_generated_diagnostics(&parser_diagnostics, &[]);
13404
13405        // Listeners receive a resolvable view of the offending token — the
13406        // ANTLR offendingSymbol contract downstream span-building error
13407        // reporters (miette-style byte-offset underlines) rely on.
13408        let recorded = diagnostics
13409            .lock()
13410            .expect("recorded diagnostics lock")
13411            .clone();
13412        insta::assert_debug_snapshot!(
13413            "recovery_diagnostics_expose_the_offending_token_to_listeners",
13414            recorded
13415        );
13416    }
13417
13418    #[test]
13419    fn recovery_diagnostics_preserve_unknown_custom_token_span() {
13420        let mut parser = mini_parser(vec![
13421            TestToken::new(7)
13422                .with_text("oops")
13423                .with_span(0, 3)
13424                .with_position(1, 2),
13425            TestToken::eof("parser-test", 4, 1, 6),
13426        ]);
13427        parser.remove_error_listeners();
13428        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13429        parser.add_error_listener(RecordingErrorListener {
13430            diagnostics: Arc::clone(&diagnostics),
13431        });
13432        let offending = parser.input.lt_id(1);
13433        assert!(offending.is_some(), "current token should be buffered");
13434
13435        parser.dispatch_parser_diagnostic(&ParserDiagnostic {
13436            line: 1,
13437            column: 2,
13438            message: "extraneous input 'oops'".to_owned(),
13439            offending,
13440        });
13441
13442        let span = {
13443            let diagnostics = diagnostics.lock().expect("recorded diagnostics lock");
13444            assert_eq!(diagnostics.len(), 1);
13445            diagnostics[0].span.clone()
13446        };
13447        assert_eq!(span, None);
13448    }
13449
13450    #[test]
13451    fn parser_leaves_token_errors_to_source_owned_listeners() {
13452        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
13453        let source = ReportingSource {
13454            source: Source {
13455                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
13456                index: 0,
13457            },
13458            diagnostics: Rc::clone(&source_diagnostics),
13459        };
13460        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
13461        parser.remove_error_listeners();
13462        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
13463        parser.add_error_listener(RecordingErrorListener {
13464            diagnostics: Arc::clone(&parser_diagnostics),
13465        });
13466        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
13467
13468        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
13469
13470        assert_eq!(*source_diagnostics.borrow(), [source_error]);
13471        assert!(
13472            parser_diagnostics
13473                .lock()
13474                .expect("recorded diagnostics lock")
13475                .is_empty()
13476        );
13477    }
13478
13479    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
13480        builder.finish().expect("valid packed parser ATN")
13481    }
13482
13483    fn nested_rule_chain_atn(depth: usize) -> Atn {
13484        nested_rule_graph_atn(depth, false, false)
13485    }
13486
13487    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
13488        assert!(depth > 0);
13489        let mut atn = ParserAtnBuilder::new(2);
13490        let mut starts = Vec::with_capacity(depth);
13491        let mut stops = Vec::with_capacity(depth);
13492        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
13493        for rule_index in 0..depth {
13494            starts.push(
13495                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
13496                    .expect("rule start")
13497                    .index(),
13498            );
13499        }
13500        for rule_index in 0..depth {
13501            stops.push(
13502                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
13503                    .expect("rule stop")
13504                    .index(),
13505            );
13506        }
13507        if consuming_follows {
13508            for rule_index in 0..depth - 1 {
13509                follows.push(
13510                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
13511                        .expect("rule follow")
13512                        .index(),
13513                );
13514            }
13515        }
13516        atn.set_rule_to_start_state(starts.clone())
13517            .expect("rule start states");
13518        atn.set_rule_to_stop_state(stops.clone())
13519            .expect("rule stop states");
13520        for rule_index in 0..depth - 1 {
13521            let follow_state = if consuming_follows {
13522                follows[rule_index]
13523            } else {
13524                stops[rule_index]
13525            };
13526            atn.add_transition(
13527                starts[rule_index],
13528                ParserTransitionSpec::Rule {
13529                    target: starts[rule_index + 1],
13530                    rule_index: rule_index + 1,
13531                    follow_state,
13532                    precedence: 0,
13533                },
13534            )
13535            .expect("nested rule transition");
13536            if branching {
13537                atn.add_transition(
13538                    starts[rule_index],
13539                    ParserTransitionSpec::Atom {
13540                        target: stops[rule_index],
13541                        label: 2,
13542                    },
13543                )
13544                .expect("dead branch transition");
13545            }
13546            if consuming_follows {
13547                atn.add_transition(
13548                    follow_state,
13549                    ParserTransitionSpec::Atom {
13550                        target: stops[rule_index],
13551                        label: 1,
13552                    },
13553                )
13554                .expect("consuming follow transition");
13555            }
13556        }
13557        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
13558        atn.add_transition(
13559            starts[depth - 1],
13560            ParserTransitionSpec::Set {
13561                target: stops[depth - 1],
13562                set: token_set,
13563            },
13564        )
13565        .expect("terminal set transition");
13566        if branching {
13567            atn.add_transition(
13568                starts[depth - 1],
13569                ParserTransitionSpec::Atom {
13570                    target: stops[depth - 1],
13571                    label: 2,
13572                },
13573            )
13574            .expect("dead leaf branch transition");
13575        }
13576        finish_atn(atn)
13577    }
13578
13579    fn ordinary_star_loop_atn() -> Atn {
13580        let mut atn = ParserAtnBuilder::new(2);
13581        for (state_number, kind, rule_index) in [
13582            (0, AtnStateKind::RuleStart, 0),
13583            (1, AtnStateKind::StarLoopEntry, 0),
13584            (2, AtnStateKind::Basic, 0),
13585            (3, AtnStateKind::StarLoopBack, 0),
13586            (4, AtnStateKind::LoopEnd, 0),
13587            (5, AtnStateKind::Basic, 0),
13588            (6, AtnStateKind::RuleStop, 0),
13589            (7, AtnStateKind::RuleStart, 1),
13590            (8, AtnStateKind::Basic, 1),
13591            (9, AtnStateKind::RuleStop, 1),
13592        ] {
13593            assert_eq!(
13594                atn.add_state(kind, Some(rule_index))
13595                    .expect("state")
13596                    .index(),
13597                state_number
13598            );
13599        }
13600        atn.set_rule_to_start_state(vec![0, 7])
13601            .expect("rule start states");
13602        atn.set_rule_to_stop_state(vec![6, 9])
13603            .expect("rule stop states");
13604        atn.add_decision_state(1).expect("decision state");
13605        atn.set_loop_back_state(4, 3).expect("loop back state");
13606        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13607            .expect("transition");
13608        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13609            .expect("transition");
13610        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
13611            .expect("transition");
13612        atn.add_transition(
13613            2,
13614            ParserTransitionSpec::Rule {
13615                target: 7,
13616                rule_index: 1,
13617                follow_state: 3,
13618                precedence: 0,
13619            },
13620        )
13621        .expect("transition");
13622        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
13623            .expect("transition");
13624        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13625            .expect("transition");
13626        atn.add_transition(
13627            5,
13628            ParserTransitionSpec::Atom {
13629                target: 6,
13630                label: TOKEN_EOF,
13631            },
13632        )
13633        .expect("transition");
13634        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13635            .expect("transition");
13636        atn.add_transition(
13637            8,
13638            ParserTransitionSpec::Atom {
13639                target: 9,
13640                label: 1,
13641            },
13642        )
13643        .expect("transition");
13644        finish_atn(atn)
13645    }
13646
13647    /// ATN for `s : (X | X X)* EOF`.
13648    fn ambiguous_ordinary_star_loop_atn() -> Atn {
13649        let mut atn = ParserAtnBuilder::new(1);
13650        for (state_number, kind) in [
13651            (0, AtnStateKind::RuleStart),
13652            (1, AtnStateKind::StarLoopEntry),
13653            (2, AtnStateKind::StarBlockStart),
13654            (3, AtnStateKind::Basic),
13655            (4, AtnStateKind::BlockEnd),
13656            (5, AtnStateKind::StarLoopBack),
13657            (6, AtnStateKind::LoopEnd),
13658            (7, AtnStateKind::Basic),
13659            (8, AtnStateKind::RuleStop),
13660        ] {
13661            assert_eq!(
13662                atn.add_state(kind, Some(0)).expect("state").index(),
13663                state_number
13664            );
13665        }
13666        atn.set_rule_to_start_state(vec![0])
13667            .expect("rule start states");
13668        atn.set_rule_to_stop_state(vec![8])
13669            .expect("rule stop states");
13670        atn.set_end_state(2, 4).expect("block end state");
13671        atn.set_loop_back_state(6, 5).expect("loop back state");
13672        atn.add_decision_state(1).expect("decision state");
13673        atn.add_decision_state(2).expect("decision state");
13674        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13675            .expect("transition");
13676        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13677            .expect("transition");
13678        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
13679            .expect("transition");
13680        atn.add_transition(
13681            2,
13682            ParserTransitionSpec::Atom {
13683                target: 4,
13684                label: 1,
13685            },
13686        )
13687        .expect("transition");
13688        atn.add_transition(
13689            2,
13690            ParserTransitionSpec::Atom {
13691                target: 3,
13692                label: 1,
13693            },
13694        )
13695        .expect("transition");
13696        atn.add_transition(
13697            3,
13698            ParserTransitionSpec::Atom {
13699                target: 4,
13700                label: 1,
13701            },
13702        )
13703        .expect("transition");
13704        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13705            .expect("transition");
13706        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
13707            .expect("transition");
13708        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13709            .expect("transition");
13710        atn.add_transition(
13711            7,
13712            ParserTransitionSpec::Atom {
13713                target: 8,
13714                label: TOKEN_EOF,
13715            },
13716        )
13717        .expect("transition");
13718        finish_atn(atn)
13719    }
13720
13721    fn ordinary_plus_loop_atn() -> Atn {
13722        let mut atn = ParserAtnBuilder::new(2);
13723        for (state_number, kind, rule_index) in [
13724            (0, AtnStateKind::RuleStart, 0),
13725            (1, AtnStateKind::Basic, 0),
13726            (2, AtnStateKind::PlusLoopBack, 0),
13727            (3, AtnStateKind::LoopEnd, 0),
13728            (4, AtnStateKind::Basic, 0),
13729            (5, AtnStateKind::RuleStop, 0),
13730            (6, AtnStateKind::RuleStart, 1),
13731            (7, AtnStateKind::Basic, 1),
13732            (8, AtnStateKind::RuleStop, 1),
13733        ] {
13734            assert_eq!(
13735                atn.add_state(kind, Some(rule_index))
13736                    .expect("state")
13737                    .index(),
13738                state_number
13739            );
13740        }
13741        atn.set_rule_to_start_state(vec![0, 6])
13742            .expect("rule start states");
13743        atn.set_rule_to_stop_state(vec![5, 8])
13744            .expect("rule stop states");
13745        atn.add_decision_state(2).expect("decision state");
13746        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13747            .expect("transition");
13748        atn.add_transition(
13749            1,
13750            ParserTransitionSpec::Rule {
13751                target: 6,
13752                rule_index: 1,
13753                follow_state: 2,
13754                precedence: 0,
13755            },
13756        )
13757        .expect("transition");
13758        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
13759            .expect("transition");
13760        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13761            .expect("transition");
13762        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13763            .expect("transition");
13764        atn.add_transition(
13765            4,
13766            ParserTransitionSpec::Atom {
13767                target: 5,
13768                label: TOKEN_EOF,
13769            },
13770        )
13771        .expect("transition");
13772        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13773            .expect("transition");
13774        atn.add_transition(
13775            7,
13776            ParserTransitionSpec::Atom {
13777                target: 8,
13778                label: 1,
13779            },
13780        )
13781        .expect("transition");
13782        finish_atn(atn)
13783    }
13784
13785    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
13786        let mut tokens = (0..count)
13787            .map(|_| TestToken::new(1).with_text("x"))
13788            .collect::<Vec<_>>();
13789        tokens.push(TestToken::eof("parser-test", count, 1, count));
13790        tokens
13791    }
13792
13793    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
13794        let mut atn = ParserAtnBuilder::new(2);
13795        assert_eq!(
13796            atn.add_state(AtnStateKind::RuleStart, Some(0))
13797                .expect("state")
13798                .index(),
13799            0
13800        );
13801        assert_eq!(
13802            atn.add_state(AtnStateKind::Basic, Some(0))
13803                .expect("state")
13804                .index(),
13805            1
13806        );
13807        assert_eq!(
13808            atn.add_state(AtnStateKind::Basic, Some(0))
13809                .expect("state")
13810                .index(),
13811            2
13812        );
13813        assert_eq!(
13814            atn.add_state(AtnStateKind::RuleStart, Some(1))
13815                .expect("state")
13816                .index(),
13817            3
13818        );
13819        atn.set_left_recursive_rule(3)
13820            .expect("left-recursive rule start");
13821        assert_eq!(
13822            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
13823                .expect("state")
13824                .index(),
13825            4
13826        );
13827        atn.set_precedence_rule_decision(4)
13828            .expect("precedence decision");
13829        assert_eq!(
13830            atn.add_state(AtnStateKind::Basic, Some(1))
13831                .expect("state")
13832                .index(),
13833            5
13834        );
13835        assert_eq!(
13836            atn.add_state(AtnStateKind::Basic, Some(1))
13837                .expect("state")
13838                .index(),
13839            6
13840        );
13841        assert_eq!(
13842            atn.add_state(AtnStateKind::LoopEnd, Some(1))
13843                .expect("state")
13844                .index(),
13845            7
13846        );
13847        assert_eq!(
13848            atn.add_state(AtnStateKind::RuleStop, Some(1))
13849                .expect("state")
13850                .index(),
13851            8
13852        );
13853        assert_eq!(
13854            atn.add_state(AtnStateKind::RuleStop, Some(0))
13855                .expect("state")
13856                .index(),
13857            9
13858        );
13859        atn.set_rule_to_start_state(vec![0, 3])
13860            .expect("rule start states");
13861        atn.set_rule_to_stop_state(vec![9, 8])
13862            .expect("rule stop states");
13863        atn.add_transition(
13864            1,
13865            ParserTransitionSpec::Rule {
13866                target: 3,
13867                rule_index: 1,
13868                follow_state: 2,
13869                precedence: 0,
13870            },
13871        )
13872        .expect("transition");
13873        atn.add_transition(
13874            2,
13875            ParserTransitionSpec::Atom {
13876                target: 9,
13877                label: caller_symbol,
13878            },
13879        )
13880        .expect("transition");
13881        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13882            .expect("transition");
13883        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
13884            .expect("transition");
13885        atn.add_transition(
13886            5,
13887            ParserTransitionSpec::Precedence {
13888                target: 6,
13889                precedence: 1,
13890            },
13891        )
13892        .expect("transition");
13893        atn.add_transition(
13894            6,
13895            ParserTransitionSpec::Atom {
13896                target: 4,
13897                label: 1,
13898            },
13899        )
13900        .expect("transition");
13901        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13902            .expect("transition");
13903        finish_atn(atn)
13904    }
13905
13906    fn labeled_left_recursive_operator_atn() -> Atn {
13907        let mut atn = ParserAtnBuilder::new(4);
13908        for (state, kind) in [
13909            (0, AtnStateKind::RuleStart),
13910            (1, AtnStateKind::BlockStart),
13911            (2, AtnStateKind::StarLoopEntry),
13912            (3, AtnStateKind::StarBlockStart),
13913            (4, AtnStateKind::Basic),
13914            (5, AtnStateKind::Basic),
13915            (6, AtnStateKind::Basic),
13916            (7, AtnStateKind::StarLoopBack),
13917            (8, AtnStateKind::LoopEnd),
13918            (9, AtnStateKind::RuleStop),
13919        ] {
13920            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13921        }
13922        atn.set_left_recursive_rule(0)
13923            .expect("left-recursive rule start");
13924        atn.set_precedence_rule_decision(2)
13925            .expect("precedence decision");
13926        atn.set_loop_back_state(8, 7).expect("loop-back state");
13927        atn.set_rule_to_start_state(vec![0])
13928            .expect("rule start states");
13929        atn.set_rule_to_stop_state(vec![9])
13930            .expect("rule stop states");
13931        for state in [1, 2, 3] {
13932            atn.add_decision_state(state).expect("decision state");
13933        }
13934        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
13935            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
13936                .expect("epsilon transition");
13937        }
13938        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
13939            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
13940                .expect("token transition");
13941        }
13942        for (target, precedence) in [(4, 2), (5, 1)] {
13943            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
13944                .expect("operator precedence");
13945        }
13946        finish_atn(atn)
13947    }
13948
13949    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
13950        let mut parser = mini_parser(vec![
13951            TestToken::new(symbol).with_text("lookahead"),
13952            TestToken::eof("parser-test", 1, 1, 1),
13953        ]);
13954        parser.rule_context_stack = vec![
13955            RuleContextFrame {
13956                rule_index: 0,
13957                invoking_state: -1,
13958            },
13959            RuleContextFrame {
13960                rule_index: 1,
13961                invoking_state: 1,
13962            },
13963        ];
13964        parser
13965    }
13966
13967    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
13968        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
13969        //   prec 2: token 1, token 1  (shift `>>`)
13970        //   prec 1: token 1           (relational `>`)
13971        let mut atn = ParserAtnBuilder::new(1);
13972        for (state, kind, rule) in [
13973            (0, AtnStateKind::RuleStart, 0),
13974            (1, AtnStateKind::StarLoopEntry, 0),
13975            (2, AtnStateKind::Basic, 0), // ops hub
13976            (3, AtnStateKind::Basic, 0), // shift prec
13977            (4, AtnStateKind::Basic, 0), // shift first >
13978            (5, AtnStateKind::Basic, 0), // shift second >
13979            (6, AtnStateKind::Basic, 0), // rel prec
13980            (7, AtnStateKind::Basic, 0), // rel >
13981            (8, AtnStateKind::LoopEnd, 0),
13982            (9, AtnStateKind::RuleStop, 0),
13983        ] {
13984            assert_eq!(
13985                atn.add_state(kind, Some(rule)).expect("state").index(),
13986                state
13987            );
13988            if state == 0 {
13989                atn.set_left_recursive_rule(state)
13990                    .expect("left-recursive rule start");
13991            } else if state == 1 {
13992                atn.set_precedence_rule_decision(state)
13993                    .expect("precedence decision");
13994            }
13995        }
13996        atn.set_rule_to_start_state(vec![0])
13997            .expect("rule start states");
13998        atn.set_rule_to_stop_state(vec![9])
13999            .expect("rule stop states");
14000        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14001            .expect("ops");
14002        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
14003            .expect("exit");
14004        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
14005            .expect("to shift");
14006        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
14007            .expect("to rel");
14008        atn.add_transition(
14009            3,
14010            ParserTransitionSpec::Precedence {
14011                target: 4,
14012                precedence: 2,
14013            },
14014        )
14015        .expect("shift prec");
14016        atn.add_transition(
14017            4,
14018            ParserTransitionSpec::Atom {
14019                target: 5,
14020                label: 1,
14021            },
14022        )
14023        .expect("shift first >");
14024        atn.add_transition(
14025            5,
14026            ParserTransitionSpec::Atom {
14027                target: 1,
14028                label: 1,
14029            },
14030        )
14031        .expect("shift second >");
14032        atn.add_transition(
14033            6,
14034            ParserTransitionSpec::Precedence {
14035                target: 7,
14036                precedence: 1,
14037            },
14038        )
14039        .expect("rel prec");
14040        atn.add_transition(
14041            7,
14042            ParserTransitionSpec::Atom {
14043                target: 1,
14044                label: 1,
14045            },
14046        )
14047        .expect("rel >");
14048        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14049            .expect("loop end");
14050        finish_atn(atn)
14051    }
14052
14053    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
14054        let mut atn = ParserAtnBuilder::new(2);
14055        for (state, kind, rule) in [
14056            (0, AtnStateKind::RuleStart, 0),
14057            (1, AtnStateKind::StarLoopEntry, 0),
14058            (2, AtnStateKind::Basic, 0),
14059            (3, AtnStateKind::Basic, 0),
14060            (4, AtnStateKind::Basic, 0),
14061            (5, AtnStateKind::Basic, 0),
14062            (6, AtnStateKind::Basic, 0),
14063            (7, AtnStateKind::Basic, 0),
14064            (8, AtnStateKind::LoopEnd, 0),
14065            (9, AtnStateKind::RuleStop, 0),
14066            (10, AtnStateKind::RuleStart, 1),
14067            (11, AtnStateKind::Basic, 1),
14068            (12, AtnStateKind::RuleStop, 1),
14069        ] {
14070            assert_eq!(
14071                atn.add_state(kind, Some(rule)).expect("state").index(),
14072                state
14073            );
14074            if state == 0 {
14075                atn.set_left_recursive_rule(state)
14076                    .expect("left-recursive rule start");
14077            } else if state == 1 {
14078                atn.set_precedence_rule_decision(state)
14079                    .expect("precedence decision");
14080            }
14081        }
14082        atn.set_rule_to_start_state(vec![0, 10])
14083            .expect("rule start states");
14084        atn.set_rule_to_stop_state(vec![9, 12])
14085            .expect("rule stop states");
14086        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14087            .expect("ops");
14088        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
14089            .expect("exit");
14090        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
14091            .expect("to shift");
14092        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
14093            .expect("to relational");
14094        atn.add_transition(
14095            3,
14096            ParserTransitionSpec::Precedence {
14097                target: 4,
14098                precedence: 2,
14099            },
14100        )
14101        .expect("shift precedence");
14102        atn.add_transition(
14103            4,
14104            ParserTransitionSpec::Rule {
14105                target: 10,
14106                rule_index: 1,
14107                follow_state: 5,
14108                precedence: 0,
14109            },
14110        )
14111        .expect("first shift token helper");
14112        atn.add_transition(
14113            5,
14114            ParserTransitionSpec::Atom {
14115                target: 1,
14116                label: 1,
14117            },
14118        )
14119        .expect("second shift token");
14120        atn.add_transition(
14121            6,
14122            ParserTransitionSpec::Precedence {
14123                target: 7,
14124                precedence: 1,
14125            },
14126        )
14127        .expect("relational precedence");
14128        atn.add_transition(
14129            7,
14130            ParserTransitionSpec::Atom {
14131                target: 1,
14132                label: 1,
14133            },
14134        )
14135        .expect("relational token");
14136        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14137            .expect("loop end");
14138        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
14139            .expect("helper entry");
14140        atn.add_transition(
14141            11,
14142            ParserTransitionSpec::Atom {
14143                target: 12,
14144                label: 1,
14145            },
14146        )
14147        .expect("first shift token");
14148        finish_atn(atn)
14149    }
14150
14151    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
14152        let mut atn = ParserAtnBuilder::new(1);
14153        for (state, kind) in [
14154            (0, AtnStateKind::RuleStart),
14155            (1, AtnStateKind::StarLoopEntry),
14156            (2, AtnStateKind::Basic),
14157            (3, AtnStateKind::Basic),
14158            (4, AtnStateKind::Basic),
14159            (5, AtnStateKind::Basic),
14160            (6, AtnStateKind::Basic),
14161            (7, AtnStateKind::Basic),
14162            (8, AtnStateKind::Basic),
14163            (9, AtnStateKind::LoopEnd),
14164            (10, AtnStateKind::RuleStop),
14165        ] {
14166            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
14167            if state == 0 {
14168                atn.set_left_recursive_rule(state)
14169                    .expect("left-recursive rule start");
14170            } else if state == 1 {
14171                atn.set_precedence_rule_decision(state)
14172                    .expect("precedence decision");
14173            }
14174        }
14175        atn.set_rule_to_start_state(vec![0])
14176            .expect("rule start states");
14177        atn.set_rule_to_stop_state(vec![10])
14178            .expect("rule stop states");
14179        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14180            .expect("ops");
14181        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
14182            .expect("exit");
14183        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
14184            .expect("to multi-token operator");
14185        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
14186            .expect("to predicate operator");
14187        atn.add_transition(
14188            3,
14189            ParserTransitionSpec::Precedence {
14190                target: 4,
14191                precedence: 2,
14192            },
14193        )
14194        .expect("multi-token precedence");
14195        atn.add_transition(
14196            4,
14197            ParserTransitionSpec::Atom {
14198                target: 5,
14199                label: 1,
14200            },
14201        )
14202        .expect("multi-token first");
14203        atn.add_transition(
14204            5,
14205            ParserTransitionSpec::Atom {
14206                target: 1,
14207                label: 1,
14208            },
14209        )
14210        .expect("multi-token second");
14211        atn.add_transition(
14212            6,
14213            ParserTransitionSpec::Precedence {
14214                target: 7,
14215                precedence: 2,
14216            },
14217        )
14218        .expect("predicate precedence");
14219        atn.add_transition(
14220            7,
14221            ParserTransitionSpec::Predicate {
14222                target: 8,
14223                rule_index: 0,
14224                pred_index: 0,
14225                context_dependent: false,
14226            },
14227        )
14228        .expect("operator predicate");
14229        atn.add_transition(
14230            8,
14231            ParserTransitionSpec::Atom {
14232                target: 1,
14233                label: 1,
14234            },
14235        )
14236        .expect("predicate single token");
14237        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14238            .expect("loop end");
14239        finish_atn(atn)
14240    }
14241
14242    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
14243        let mut atn = ParserAtnBuilder::new(2);
14244        for (state, kind, rule) in [
14245            (0, AtnStateKind::RuleStart, 0),
14246            (1, AtnStateKind::StarLoopEntry, 0),
14247            (2, AtnStateKind::Basic, 0),
14248            (3, AtnStateKind::Basic, 0),
14249            (4, AtnStateKind::Basic, 0),
14250            (5, AtnStateKind::LoopEnd, 0),
14251            (6, AtnStateKind::RuleStop, 0),
14252            (7, AtnStateKind::RuleStart, 1),
14253            (8, AtnStateKind::RuleStop, 1),
14254            (9, AtnStateKind::Basic, 1),
14255        ] {
14256            assert_eq!(
14257                atn.add_state(kind, Some(rule)).expect("state").index(),
14258                state
14259            );
14260            if state == 0 {
14261                atn.set_left_recursive_rule(state)
14262                    .expect("left-recursive rule start");
14263            } else if state == 1 {
14264                atn.set_precedence_rule_decision(state)
14265                    .expect("precedence decision");
14266            }
14267        }
14268        atn.set_rule_to_start_state(vec![0, 7])
14269            .expect("rule start states");
14270        atn.set_rule_to_stop_state(vec![6, 8])
14271            .expect("rule stop states");
14272        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14273            .expect("transition");
14274        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
14275            .expect("transition");
14276        atn.add_transition(
14277            2,
14278            ParserTransitionSpec::Precedence {
14279                target: 3,
14280                precedence: 3,
14281            },
14282        )
14283        .expect("transition");
14284        atn.add_transition(
14285            3,
14286            ParserTransitionSpec::Rule {
14287                target: 7,
14288                rule_index: 1,
14289                follow_state: 4,
14290                precedence: 0,
14291            },
14292        )
14293        .expect("transition");
14294        atn.add_transition(
14295            4,
14296            ParserTransitionSpec::Atom {
14297                target: 1,
14298                label: 1,
14299            },
14300        )
14301        .expect("transition");
14302        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14303            .expect("transition");
14304        atn.add_transition(
14305            7,
14306            ParserTransitionSpec::Precedence {
14307                target: 9,
14308                precedence: 1,
14309            },
14310        )
14311        .expect("transition");
14312        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
14313            .expect("transition");
14314        finish_atn(atn)
14315    }
14316
14317    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
14318        let mut atn = ParserAtnBuilder::new(2);
14319        for (state, kind) in [
14320            (0, AtnStateKind::RuleStart),
14321            (1, AtnStateKind::StarLoopEntry),
14322            (2, AtnStateKind::Basic),
14323            (3, AtnStateKind::Basic),
14324            (4, AtnStateKind::Basic),
14325            (5, AtnStateKind::LoopEnd),
14326            (6, AtnStateKind::RuleStop),
14327        ] {
14328            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
14329            if state == 0 {
14330                atn.set_left_recursive_rule(state)
14331                    .expect("left-recursive rule start");
14332            } else if state == 1 {
14333                atn.set_precedence_rule_decision(state)
14334                    .expect("precedence decision");
14335            }
14336        }
14337        atn.set_rule_to_start_state(vec![0])
14338            .expect("rule start states");
14339        atn.set_rule_to_stop_state(vec![6])
14340            .expect("rule stop states");
14341        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14342            .expect("transition");
14343        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
14344            .expect("transition");
14345        atn.add_transition(
14346            2,
14347            ParserTransitionSpec::Precedence {
14348                target: 3,
14349                precedence: 1,
14350            },
14351        )
14352        .expect("transition");
14353        atn.add_transition(
14354            3,
14355            ParserTransitionSpec::Predicate {
14356                target: 4,
14357                rule_index: 0,
14358                pred_index: 0,
14359                context_dependent: false,
14360            },
14361        )
14362        .expect("transition");
14363        atn.add_transition(
14364            4,
14365            ParserTransitionSpec::Atom {
14366                target: 1,
14367                label: 1,
14368            },
14369        )
14370        .expect("transition");
14371        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14372            .expect("transition");
14373        finish_atn(atn)
14374    }
14375
14376    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
14377        let mut atn = ParserAtnBuilder::new(2);
14378        for (state, kind, rule) in [
14379            (0, AtnStateKind::RuleStart, 0),
14380            (1, AtnStateKind::Basic, 0),
14381            (2, AtnStateKind::Basic, 0),
14382            (3, AtnStateKind::Basic, 0),
14383            (4, AtnStateKind::RuleStop, 0),
14384            (5, AtnStateKind::RuleStart, 1),
14385            (6, AtnStateKind::StarLoopEntry, 1),
14386            (7, AtnStateKind::Basic, 1),
14387            (8, AtnStateKind::Basic, 1),
14388            (9, AtnStateKind::LoopEnd, 1),
14389            (10, AtnStateKind::RuleStop, 1),
14390            (11, AtnStateKind::RuleStart, 2),
14391            (12, AtnStateKind::RuleStop, 2),
14392        ] {
14393            assert_eq!(
14394                atn.add_state(kind, Some(rule)).expect("state").index(),
14395                state
14396            );
14397            if state == 5 {
14398                atn.set_left_recursive_rule(state)
14399                    .expect("left-recursive rule start");
14400            } else if state == 6 {
14401                atn.set_precedence_rule_decision(state)
14402                    .expect("precedence decision");
14403            }
14404        }
14405        atn.set_rule_to_start_state(vec![0, 5, 11])
14406            .expect("rule start states");
14407        atn.set_rule_to_stop_state(vec![4, 10, 12])
14408            .expect("rule stop states");
14409        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14410            .expect("transition");
14411        atn.add_transition(
14412            1,
14413            ParserTransitionSpec::Rule {
14414                target: 5,
14415                rule_index: 1,
14416                follow_state: 2,
14417                precedence: 0,
14418            },
14419        )
14420        .expect("transition");
14421        atn.add_transition(
14422            2,
14423            ParserTransitionSpec::Rule {
14424                target: 11,
14425                rule_index: 2,
14426                follow_state: 3,
14427                precedence: 0,
14428            },
14429        )
14430        .expect("transition");
14431        atn.add_transition(
14432            3,
14433            ParserTransitionSpec::Atom {
14434                target: 4,
14435                label: caller_symbol,
14436            },
14437        )
14438        .expect("transition");
14439        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
14440            .expect("transition");
14441        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
14442            .expect("transition");
14443        atn.add_transition(
14444            7,
14445            ParserTransitionSpec::Precedence {
14446                target: 8,
14447                precedence: 1,
14448            },
14449        )
14450        .expect("transition");
14451        atn.add_transition(
14452            8,
14453            ParserTransitionSpec::Atom {
14454                target: 6,
14455                label: 1,
14456            },
14457        )
14458        .expect("transition");
14459        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14460            .expect("transition");
14461        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
14462            .expect("transition");
14463        finish_atn(atn)
14464    }
14465
14466    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
14467        let mut atn = ParserAtnBuilder::new(2);
14468        for (state, kind, rule) in [
14469            (0, AtnStateKind::RuleStart, 0),
14470            (1, AtnStateKind::Basic, 0),
14471            (2, AtnStateKind::Basic, 0),
14472            (3, AtnStateKind::RuleStop, 0),
14473            (4, AtnStateKind::RuleStart, 1),
14474            (5, AtnStateKind::Basic, 1),
14475            (6, AtnStateKind::Basic, 1),
14476            (7, AtnStateKind::RuleStop, 1),
14477            (8, AtnStateKind::RuleStart, 2),
14478            (9, AtnStateKind::StarLoopEntry, 2),
14479            (10, AtnStateKind::Basic, 2),
14480            (11, AtnStateKind::Basic, 2),
14481            (12, AtnStateKind::LoopEnd, 2),
14482            (13, AtnStateKind::RuleStop, 2),
14483        ] {
14484            assert_eq!(
14485                atn.add_state(kind, Some(rule)).expect("state").index(),
14486                state
14487            );
14488            if state == 8 {
14489                atn.set_left_recursive_rule(state)
14490                    .expect("left-recursive rule start");
14491            } else if state == 9 {
14492                atn.set_precedence_rule_decision(state)
14493                    .expect("precedence decision");
14494            }
14495        }
14496        atn.set_rule_to_start_state(vec![0, 4, 8])
14497            .expect("rule start states");
14498        atn.set_rule_to_stop_state(vec![3, 7, 13])
14499            .expect("rule stop states");
14500        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14501            .expect("transition");
14502        atn.add_transition(
14503            1,
14504            ParserTransitionSpec::Rule {
14505                target: 4,
14506                rule_index: 1,
14507                follow_state: 2,
14508                precedence: 0,
14509            },
14510        )
14511        .expect("transition");
14512        atn.add_transition(
14513            2,
14514            ParserTransitionSpec::Atom {
14515                target: 3,
14516                label: caller_symbol,
14517            },
14518        )
14519        .expect("transition");
14520        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14521            .expect("transition");
14522        atn.add_transition(
14523            5,
14524            ParserTransitionSpec::Rule {
14525                target: 8,
14526                rule_index: 2,
14527                follow_state: 6,
14528                precedence: 0,
14529            },
14530        )
14531        .expect("transition");
14532        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
14533            .expect("transition");
14534        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14535            .expect("transition");
14536        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
14537            .expect("transition");
14538        atn.add_transition(
14539            10,
14540            ParserTransitionSpec::Precedence {
14541                target: 11,
14542                precedence: 1,
14543            },
14544        )
14545        .expect("transition");
14546        atn.add_transition(
14547            11,
14548            ParserTransitionSpec::Atom {
14549                target: 9,
14550                label: 1,
14551            },
14552        )
14553        .expect("transition");
14554        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
14555            .expect("transition");
14556        finish_atn(atn)
14557    }
14558
14559    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
14560        let mut atn = ParserAtnBuilder::new(2);
14561        for (state, kind, rule) in [
14562            (0, AtnStateKind::RuleStart, 0),
14563            (1, AtnStateKind::Basic, 0),
14564            (2, AtnStateKind::Basic, 0),
14565            (3, AtnStateKind::RuleStop, 0),
14566            (4, AtnStateKind::RuleStart, 1),
14567            (5, AtnStateKind::StarLoopEntry, 1),
14568            (6, AtnStateKind::Basic, 1),
14569            (7, AtnStateKind::Basic, 1),
14570            (8, AtnStateKind::Basic, 1),
14571            (9, AtnStateKind::Basic, 1),
14572            (10, AtnStateKind::LoopEnd, 1),
14573            (11, AtnStateKind::RuleStop, 1),
14574        ] {
14575            assert_eq!(
14576                atn.add_state(kind, Some(rule)).expect("state").index(),
14577                state
14578            );
14579            if state == 4 {
14580                atn.set_left_recursive_rule(state)
14581                    .expect("left-recursive rule start");
14582            } else if state == 5 {
14583                atn.set_precedence_rule_decision(state)
14584                    .expect("precedence decision");
14585            }
14586        }
14587        atn.set_rule_to_start_state(vec![0, 4])
14588            .expect("rule start states");
14589        atn.set_rule_to_stop_state(vec![3, 11])
14590            .expect("rule stop states");
14591        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14592            .expect("transition");
14593        atn.add_transition(
14594            1,
14595            ParserTransitionSpec::Rule {
14596                target: 4,
14597                rule_index: 1,
14598                follow_state: 2,
14599                precedence: 0,
14600            },
14601        )
14602        .expect("transition");
14603        atn.add_transition(
14604            2,
14605            ParserTransitionSpec::Atom {
14606                target: 3,
14607                label: caller_symbol,
14608            },
14609        )
14610        .expect("transition");
14611        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14612            .expect("transition");
14613        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
14614            .expect("transition");
14615        atn.add_transition(
14616            6,
14617            ParserTransitionSpec::Precedence {
14618                target: 7,
14619                precedence: 1,
14620            },
14621        )
14622        .expect("transition");
14623        atn.add_transition(
14624            7,
14625            ParserTransitionSpec::Atom {
14626                target: 8,
14627                label: 1,
14628            },
14629        )
14630        .expect("transition");
14631        atn.add_transition(
14632            8,
14633            ParserTransitionSpec::Rule {
14634                target: 4,
14635                rule_index: 1,
14636                follow_state: 9,
14637                precedence: 2,
14638            },
14639        )
14640        .expect("transition");
14641        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
14642            .expect("transition");
14643        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
14644            .expect("transition");
14645        finish_atn(atn)
14646    }
14647
14648    #[test]
14649    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
14650        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
14651        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
14652
14653        let mut overlapping = parser_inside_left_recursive_callee(1);
14654        assert_eq!(
14655            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
14656            None
14657        );
14658
14659        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
14660        assert_eq!(
14661            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14662            Some(true)
14663        );
14664
14665        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
14666        assert_eq!(
14667            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14668            Some(false)
14669        );
14670
14671        assert_eq!(
14672            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14673            Some(true),
14674            "overlap results must not leak across ATNs"
14675        );
14676    }
14677
14678    #[test]
14679    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
14680        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
14681        let mut parser = mini_parser(vec![
14682            TestToken::new(1).with_text("operator"),
14683            TestToken::eof("parser-test", 1, 1, 1),
14684        ]);
14685        parser.rule_context_stack = vec![RuleContextFrame {
14686            rule_index: 0,
14687            invoking_state: -1,
14688        }];
14689
14690        assert_eq!(
14691            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14692            Some(true)
14693        );
14694        assert_eq!(
14695            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14696            Some(true),
14697            "cached operator lookahead must preserve the nullable prefix return path"
14698        );
14699        assert_eq!(
14700            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14701            Some(true),
14702            "the nullable child must use its rule-call precedence, not the caller precedence"
14703        );
14704    }
14705
14706    #[test]
14707    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
14708        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
14709        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
14710        // must defer so StarLoopEntry adaptive predict can exit when the second
14711        // `>` is absent (as in `a < b > c`).
14712        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
14713        let mut parser = mini_parser(vec![
14714            TestToken::new(1).with_text(">"),
14715            TestToken::new(2).with_text("id"),
14716            TestToken::eof("parser-test", 1, 1, 1),
14717        ]);
14718        parser.rule_context_stack = vec![RuleContextFrame {
14719            rule_index: 0,
14720            invoking_state: -1,
14721        }];
14722
14723        assert_eq!(
14724            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14725            Some(true),
14726            "at low precedence relational `>` is a single-token operator"
14727        );
14728        assert_eq!(
14729            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
14730            Some(true),
14731            "relational remains single-token at its own precedence"
14732        );
14733        assert_eq!(
14734            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14735            None,
14736            "at shift precedence, bare `>` must not force enter"
14737        );
14738    }
14739
14740    #[test]
14741    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
14742        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
14743        let mut parser = mini_parser(vec![
14744            TestToken::new(1).with_text(">"),
14745            TestToken::new(2).with_text("id"),
14746            TestToken::eof("parser-test", 1, 1, 1),
14747        ]);
14748        parser.rule_context_stack = vec![RuleContextFrame {
14749            rule_index: 0,
14750            invoking_state: -1,
14751        }];
14752
14753        assert_eq!(
14754            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14755            Some(true),
14756            "the direct relational alternative remains a one-token operator"
14757        );
14758        assert_eq!(
14759            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14760            None,
14761            "a token matched in the helper rule must return to the second shift token"
14762        );
14763    }
14764
14765    #[test]
14766    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
14767        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
14768        let mut parser = mini_parser(vec![
14769            TestToken::new(1).with_text(">"),
14770            TestToken::new(2).with_text("id"),
14771            TestToken::eof("parser-test", 1, 1, 1),
14772        ]);
14773        parser.rule_context_stack = vec![RuleContextFrame {
14774            rule_index: 0,
14775            invoking_state: -1,
14776        }];
14777
14778        assert_eq!(
14779            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14780            None,
14781            "a predicate-gated single-token path must not be hidden by a multi-token path"
14782        );
14783    }
14784
14785    #[test]
14786    fn left_recursive_loop_defers_predicate_guarded_operator() {
14787        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
14788        let mut parser = mini_parser_with_hooks(
14789            vec![
14790                TestToken::new(1).with_text("operator"),
14791                TestToken::eof("parser-test", 1, 1, 1),
14792            ],
14793            RejectingPredicateHooks::default(),
14794        );
14795        parser.rule_context_stack = vec![RuleContextFrame {
14796            rule_index: 0,
14797            invoking_state: -1,
14798        }];
14799
14800        assert_eq!(
14801            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14802            None,
14803            "a false predicate must be evaluated before entering the operator alternative"
14804        );
14805        assert_eq!(
14806            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14807            None,
14808            "cached predicate-dependent lookahead must keep deferring"
14809        );
14810    }
14811
14812    #[test]
14813    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
14814        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
14815        let mut parser = parser_inside_left_recursive_callee(1);
14816
14817        assert_eq!(
14818            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14819            None
14820        );
14821        assert_eq!(
14822            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14823            None,
14824            "the cached overlap must preserve the nullable child return path"
14825        );
14826    }
14827
14828    #[test]
14829    fn left_recursive_loop_defers_through_nullable_parent_return() {
14830        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
14831        let mut parser = mini_parser(vec![
14832            TestToken::new(1).with_text("lookahead"),
14833            TestToken::eof("parser-test", 1, 1, 1),
14834        ]);
14835        parser.rule_context_stack = vec![
14836            RuleContextFrame {
14837                rule_index: 0,
14838                invoking_state: -1,
14839            },
14840            RuleContextFrame {
14841                rule_index: 1,
14842                invoking_state: 1,
14843            },
14844            RuleContextFrame {
14845                rule_index: 2,
14846                invoking_state: 5,
14847            },
14848        ];
14849
14850        assert_eq!(
14851            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14852            None,
14853            "a nullable caller must unwind to its parent's consuming follow path"
14854        );
14855        assert_eq!(
14856            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14857            None,
14858            "the caller-overlap cache must not retain a false negative"
14859        );
14860    }
14861
14862    #[test]
14863    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
14864        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
14865        let mut parser = mini_parser(vec![
14866            TestToken::new(1).with_text("lookahead"),
14867            TestToken::eof("parser-test", 1, 1, 1),
14868        ]);
14869        parser.rule_context_stack = vec![
14870            RuleContextFrame {
14871                rule_index: 0,
14872                invoking_state: -1,
14873            },
14874            RuleContextFrame {
14875                rule_index: 1,
14876                invoking_state: 1,
14877            },
14878            RuleContextFrame {
14879                rule_index: 1,
14880                invoking_state: 8,
14881            },
14882        ];
14883
14884        assert_eq!(
14885            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14886            None,
14887            "a recursive operand return must preserve its parent caller context"
14888        );
14889        assert_eq!(
14890            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14891            None,
14892            "the caller-overlap cache must preserve the loop-boundary return"
14893        );
14894    }
14895
14896    fn token_then_eof_atn() -> Atn {
14897        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14898            4, 1, 2, // version, parser, max token type
14899            3, // states
14900            2, 0, // rule start
14901            1, 0, // basic
14902            7, 0, // rule stop
14903            0, // non-greedy states
14904            0, // precedence states
14905            1, // rules
14906            0, // rule 0 start
14907            0, // modes
14908            0, // sets
14909            2, // transitions
14910            0, 1, 5, 1, 0, 0, // match token 1
14911            1, 2, 5, -1, 0, 0, // match EOF
14912            0, // decisions
14913        ]))
14914        .deserialize_parser()
14915        .expect("artificial parser ATN should deserialize")
14916    }
14917
14918    fn epsilon_cycle_atn() -> Atn {
14919        let mut atn = ParserAtnBuilder::new(1);
14920        for (state_number, kind) in [
14921            (0, AtnStateKind::RuleStart),
14922            (1, AtnStateKind::Basic),
14923            (2, AtnStateKind::RuleStop),
14924        ] {
14925            assert_eq!(
14926                atn.add_state(kind, Some(0)).expect("state").index(),
14927                state_number
14928            );
14929        }
14930        atn.set_rule_to_start_state(vec![0])
14931            .expect("rule start states");
14932        atn.set_rule_to_stop_state(vec![2])
14933            .expect("rule stop states");
14934        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14935            .expect("transition");
14936        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
14937            .expect("self-cycle transition");
14938        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14939            .expect("exit transition");
14940        finish_atn(atn)
14941    }
14942
14943    fn eof_then_action_atn() -> Atn {
14944        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14945            4, 1, 1, // version, parser, max token type
14946            3, // states
14947            2, 0, // rule start
14948            1, 0, // basic
14949            7, 0, // rule stop
14950            0, // non-greedy states
14951            0, // precedence states
14952            1, // rules
14953            0, // rule 0 start
14954            0, // modes
14955            0, // sets
14956            2, // transitions
14957            0, 1, 5, -1, 0, 0, // match EOF
14958            1, 2, 6, 0, 0, 0, // parser action
14959            0, // decisions
14960        ]))
14961        .deserialize_parser()
14962        .expect("artificial parser ATN should deserialize")
14963    }
14964
14965    fn noop_action_then_token_then_eof_atn() -> Atn {
14966        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14967            4, 1, 2, // version, parser, max token type
14968            4, // states
14969            2, 0, // rule start
14970            1, 0, // basic
14971            1, 0, // basic
14972            7, 0, // rule stop
14973            0, // non-greedy states
14974            0, // precedence states
14975            1, // rules
14976            0, // rule 0 start
14977            0, // modes
14978            0, // sets
14979            3, // transitions
14980            0, 1, 6, 0, -1, 0, // no-op parser action
14981            1, 2, 5, 1, 0, 0, // match token 1
14982            2, 3, 5, -1, 0, 0, // match EOF
14983            0, // decisions
14984        ]))
14985        .deserialize_parser()
14986        .expect("artificial no-op action ATN should deserialize")
14987    }
14988
14989    fn two_alt_decision_atn() -> Atn {
14990        let mut atn = ParserAtnBuilder::new(2);
14991        assert_eq!(
14992            atn.add_state(AtnStateKind::RuleStart, Some(0))
14993                .expect("state")
14994                .index(),
14995            0
14996        );
14997        assert_eq!(
14998            atn.add_state(AtnStateKind::BlockStart, Some(0))
14999                .expect("state")
15000                .index(),
15001            1
15002        );
15003        assert_eq!(
15004            atn.add_state(AtnStateKind::Basic, Some(0))
15005                .expect("state")
15006                .index(),
15007            2
15008        );
15009        assert_eq!(
15010            atn.add_state(AtnStateKind::Basic, Some(0))
15011                .expect("state")
15012                .index(),
15013            3
15014        );
15015        assert_eq!(
15016            atn.add_state(AtnStateKind::BlockEnd, Some(0))
15017                .expect("state")
15018                .index(),
15019            4
15020        );
15021        assert_eq!(
15022            atn.add_state(AtnStateKind::RuleStop, Some(0))
15023                .expect("state")
15024                .index(),
15025            5
15026        );
15027        atn.set_rule_to_start_state(vec![0])
15028            .expect("rule start states");
15029        atn.set_rule_to_stop_state(vec![5])
15030            .expect("rule stop states");
15031        atn.add_decision_state(1).expect("decision state");
15032        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15033            .expect("transition");
15034        atn.add_transition(
15035            1,
15036            ParserTransitionSpec::Atom {
15037                target: 2,
15038                label: 1,
15039            },
15040        )
15041        .expect("transition");
15042        atn.add_transition(
15043            1,
15044            ParserTransitionSpec::Atom {
15045                target: 3,
15046                label: 2,
15047            },
15048        )
15049        .expect("transition");
15050        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
15051            .expect("transition");
15052        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15053            .expect("transition");
15054        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15055            .expect("transition");
15056        finish_atn(atn)
15057    }
15058
15059    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
15060    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
15061    fn optional_then_b_eof_atn() -> Atn {
15062        let mut atn = ParserAtnBuilder::new(3);
15063        assert_eq!(
15064            atn.add_state(AtnStateKind::RuleStart, Some(0))
15065                .expect("state")
15066                .index(),
15067            0
15068        );
15069        assert_eq!(
15070            atn.add_state(AtnStateKind::BlockStart, Some(0))
15071                .expect("state")
15072                .index(),
15073            1
15074        );
15075        assert_eq!(
15076            atn.add_state(AtnStateKind::Basic, Some(0))
15077                .expect("state")
15078                .index(),
15079            2
15080        );
15081        assert_eq!(
15082            atn.add_state(AtnStateKind::Basic, Some(0))
15083                .expect("state")
15084                .index(),
15085            3
15086        );
15087        assert_eq!(
15088            atn.add_state(AtnStateKind::Basic, Some(0))
15089                .expect("state")
15090                .index(),
15091            4
15092        );
15093        assert_eq!(
15094            atn.add_state(AtnStateKind::RuleStop, Some(0))
15095                .expect("state")
15096                .index(),
15097            5
15098        );
15099        atn.set_rule_to_start_state(vec![0])
15100            .expect("rule start states");
15101        atn.set_rule_to_stop_state(vec![5])
15102            .expect("rule stop states");
15103        atn.add_decision_state(1).expect("decision state");
15104        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15105            .expect("transition");
15106        // Optional block: match A then fall through, or skip straight to state 3.
15107        atn.add_transition(
15108            1,
15109            ParserTransitionSpec::Atom {
15110                target: 3,
15111                label: 1,
15112            },
15113        )
15114        .expect("transition");
15115        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
15116            .expect("transition");
15117        // Match B, then EOF.
15118        atn.add_transition(
15119            3,
15120            ParserTransitionSpec::Atom {
15121                target: 4,
15122                label: 2,
15123            },
15124        )
15125        .expect("transition");
15126        atn.add_transition(
15127            4,
15128            ParserTransitionSpec::Atom {
15129                target: 5,
15130                label: TOKEN_EOF,
15131            },
15132        )
15133        .expect("transition");
15134        finish_atn(atn)
15135    }
15136
15137    #[test]
15138    fn sync_decision_deletes_only_a_single_token() {
15139        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
15140        // expected. `(A)? B EOF` at the optional-block decision:
15141        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
15142        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
15143        //               without consuming and records the expected set for the
15144        //               subsequent mismatch (the parser must not over-consume both
15145        //               `C`s and accept the input).
15146        let atn = optional_then_b_eof_atn();
15147
15148        let mut single = mini_parser(vec![
15149            TestToken::new(3).with_text("c"),
15150            TestToken::new(2).with_text("b"),
15151            TestToken::eof("parser-test", 1, 2, 2),
15152        ]);
15153        single.rule_context_stack = vec![RuleContextFrame {
15154            rule_index: 0,
15155            invoking_state: 0,
15156        }];
15157        let children = single
15158            .sync_decision(&atn, 1, true, false)
15159            .expect("single extraneous token recovers");
15160        assert_eq!(children.len(), 1);
15161        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
15162        assert_eq!(single.number_of_syntax_errors(), 1);
15163        // Exactly one token consumed (the cursor now sits on `b`).
15164        assert_eq!(single.la(1), 2);
15165
15166        let mut double = mini_parser(vec![
15167            TestToken::new(3).with_text("c"),
15168            TestToken::new(3).with_text("c"),
15169            TestToken::new(2).with_text("b"),
15170            TestToken::eof("parser-test", 1, 3, 3),
15171        ]);
15172        double.rule_context_stack = vec![RuleContextFrame {
15173            rule_index: 0,
15174            invoking_state: 0,
15175        }];
15176        let result = double.sync_decision(&atn, 1, true, false);
15177        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
15178        // consume either `c`. It reports the mismatch at the first `c` (so the parser
15179        // does not over-consume both and accept the input). Nothing is consumed, so
15180        // the cursor still sits on the first `c` for rule-level recovery.
15181        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
15182        match error {
15183            AntlrError::ParserError { message, .. } => {
15184                assert!(message.starts_with("mismatched input"), "got: {message}");
15185            }
15186            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
15187        }
15188        assert_eq!(double.la(1), 3);
15189    }
15190
15191    /// The real serialized ATN that `antlr4-rust-gen` emits for
15192    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
15193    /// the loop is `EOF`. The loop decision is state 5.
15194    fn star_loop_then_eof_atn() -> Atn {
15195        AtnDeserializer::new(&SerializedAtn::from_i32(&[
15196            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,
15197            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,
15198            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,
15199            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
15200        ]))
15201        .deserialize_parser()
15202        .expect("star-loop-then-EOF ATN should deserialize")
15203    }
15204
15205    /// ATN for `s : a+ Y ; a : X ;`.
15206    ///
15207    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
15208    /// `+` loop must not treat that zero-width child as a successful iteration
15209    /// and then re-enter the loop at the same token index.
15210    fn plus_loop_with_recovering_body_atn() -> Atn {
15211        let mut atn = ParserAtnBuilder::new(2);
15212        assert_eq!(
15213            atn.add_state(AtnStateKind::RuleStart, Some(0))
15214                .expect("state")
15215                .index(),
15216            0
15217        );
15218        assert_eq!(
15219            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
15220                .expect("state")
15221                .index(),
15222            1
15223        );
15224        assert_eq!(
15225            atn.add_state(AtnStateKind::Basic, Some(0))
15226                .expect("state")
15227                .index(),
15228            2
15229        );
15230        assert_eq!(
15231            atn.add_state(AtnStateKind::BlockEnd, Some(0))
15232                .expect("state")
15233                .index(),
15234            3
15235        );
15236        assert_eq!(
15237            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
15238                .expect("state")
15239                .index(),
15240            4
15241        );
15242        assert_eq!(
15243            atn.add_state(AtnStateKind::LoopEnd, Some(0))
15244                .expect("state")
15245                .index(),
15246            5
15247        );
15248        assert_eq!(
15249            atn.add_state(AtnStateKind::RuleStop, Some(0))
15250                .expect("state")
15251                .index(),
15252            6
15253        );
15254        assert_eq!(
15255            atn.add_state(AtnStateKind::RuleStart, Some(1))
15256                .expect("state")
15257                .index(),
15258            7
15259        );
15260        assert_eq!(
15261            atn.add_state(AtnStateKind::Basic, Some(1))
15262                .expect("state")
15263                .index(),
15264            8
15265        );
15266        assert_eq!(
15267            atn.add_state(AtnStateKind::RuleStop, Some(1))
15268                .expect("state")
15269                .index(),
15270            9
15271        );
15272        atn.set_rule_to_start_state(vec![0, 7])
15273            .expect("rule start states");
15274        atn.set_rule_to_stop_state(vec![6, 9])
15275            .expect("rule stop states");
15276        atn.set_end_state(1, 3).expect("block end state");
15277        atn.set_loop_back_state(5, 4).expect("loop back state");
15278        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15279            .expect("transition");
15280        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15281            .expect("transition");
15282        atn.add_transition(
15283            2,
15284            ParserTransitionSpec::Rule {
15285                target: 7,
15286                rule_index: 1,
15287                follow_state: 3,
15288                precedence: 0,
15289            },
15290        )
15291        .expect("transition");
15292        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15293            .expect("transition");
15294        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
15295            .expect("transition");
15296        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15297            .expect("transition");
15298        atn.add_transition(
15299            5,
15300            ParserTransitionSpec::Atom {
15301                target: 6,
15302                label: 2,
15303            },
15304        )
15305        .expect("transition");
15306        atn.add_transition(
15307            7,
15308            ParserTransitionSpec::Atom {
15309                target: 8,
15310                label: 1,
15311            },
15312        )
15313        .expect("transition");
15314        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15315            .expect("transition");
15316        finish_atn(atn)
15317    }
15318
15319    #[test]
15320    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
15321        let atn = plus_loop_with_recovering_body_atn();
15322        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15323
15324        let error = parser
15325            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15326            .expect_err("EOF recovery should report a bounded mismatch");
15327
15328        let AntlrError::ParserError { message, .. } = error else {
15329            panic!("expected ParserError, got {error:?}");
15330        };
15331        insta::assert_snapshot!(message, @"mismatched input '<EOF>' expecting {'x', 2}");
15332        assert_eq!(parser.number_of_syntax_errors(), 1);
15333        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
15334    }
15335
15336    #[test]
15337    fn sync_decision_deletes_token_before_eof_at_loop_back() {
15338        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
15339        // At the loop ENTRY (loop_back = false) a single unexpected token before
15340        // EOF is deleted as an error node (then the generated EOF match consumes
15341        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
15342        // EOF must be a valid scan-stop for this to fire.
15343        let atn = star_loop_then_eof_atn();
15344        let mut parser = mini_parser(vec![
15345            TestToken::new(2).with_text("c"),
15346            TestToken::eof("parser-test", 1, 1, 1),
15347        ]);
15348        parser.rule_context_stack = vec![RuleContextFrame {
15349            rule_index: 0,
15350            invoking_state: 0,
15351        }];
15352        let children = parser
15353            .sync_decision(&atn, 5, true, false)
15354            .expect("single token before EOF recovers");
15355        assert_eq!(children.len(), 1);
15356        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
15357        assert_eq!(parser.number_of_syntax_errors(), 1);
15358        assert_eq!(
15359            parser.la(1),
15360            TOKEN_EOF,
15361            "EOF is left for the rule's EOF match"
15362        );
15363    }
15364
15365    #[test]
15366    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
15367        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
15368        // single-token deletion, which fails because LA(2) = `c` is not expected —
15369        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
15370        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
15371        let atn = star_loop_then_eof_atn();
15372        let mut parser = mini_parser(vec![
15373            TestToken::new(2).with_text("c"),
15374            TestToken::new(2).with_text("c"),
15375            TestToken::eof("parser-test", 1, 2, 2),
15376        ]);
15377        parser.rule_context_stack = vec![RuleContextFrame {
15378            rule_index: 0,
15379            invoking_state: 0,
15380        }];
15381        let error = parser
15382            .sync_decision(&atn, 5, true, false)
15383            .expect_err("two tokens at the loop entry must not be deleted");
15384        match error {
15385            AntlrError::ParserError { message, .. } => {
15386                assert!(message.starts_with("mismatched input"), "got: {message}");
15387            }
15388            other => panic!("expected mismatched-input ParserError, got {other:?}"),
15389        }
15390        assert_eq!(
15391            parser.la(1),
15392            2,
15393            "nothing consumed; cursor still on first `c`"
15394        );
15395    }
15396
15397    #[test]
15398    fn sync_decision_consumes_until_eof_at_loop_back() {
15399        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
15400        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
15401        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
15402        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
15403        // post-`a` state directly: `c c <EOF>` with loop_back = true.
15404        let atn = star_loop_then_eof_atn();
15405        let mut parser = mini_parser(vec![
15406            TestToken::new(2).with_text("c"),
15407            TestToken::new(2).with_text("c"),
15408            TestToken::eof("parser-test", 1, 2, 2),
15409        ]);
15410        parser.rule_context_stack = vec![RuleContextFrame {
15411            rule_index: 0,
15412            invoking_state: 0,
15413        }];
15414        let children = parser
15415            .sync_decision(&atn, 5, false, true)
15416            .expect("loop-back multi-token deletion recovers onto EOF");
15417        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
15418        assert!(
15419            children
15420                .iter()
15421                .all(|child| parser.node(*child).kind() == NodeKind::Error)
15422        );
15423        assert_eq!(parser.number_of_syntax_errors(), 1);
15424        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
15425    }
15426
15427    fn predicate_after_token_atn() -> Atn {
15428        let mut atn = ParserAtnBuilder::new(2);
15429        assert_eq!(
15430            atn.add_state(AtnStateKind::RuleStart, Some(0))
15431                .expect("state")
15432                .index(),
15433            0
15434        );
15435        assert_eq!(
15436            atn.add_state(AtnStateKind::Basic, Some(0))
15437                .expect("state")
15438                .index(),
15439            1
15440        );
15441        assert_eq!(
15442            atn.add_state(AtnStateKind::Basic, Some(0))
15443                .expect("state")
15444                .index(),
15445            2
15446        );
15447        assert_eq!(
15448            atn.add_state(AtnStateKind::Basic, Some(0))
15449                .expect("state")
15450                .index(),
15451            3
15452        );
15453        assert_eq!(
15454            atn.add_state(AtnStateKind::RuleStop, Some(0))
15455                .expect("state")
15456                .index(),
15457            4
15458        );
15459        atn.set_rule_to_start_state(vec![0])
15460            .expect("rule start states");
15461        atn.set_rule_to_stop_state(vec![4])
15462            .expect("rule stop states");
15463        atn.add_transition(
15464            0,
15465            ParserTransitionSpec::Atom {
15466                target: 1,
15467                label: 1,
15468            },
15469        )
15470        .expect("transition");
15471        atn.add_transition(
15472            1,
15473            ParserTransitionSpec::Predicate {
15474                target: 2,
15475                rule_index: 0,
15476                pred_index: 0,
15477                context_dependent: false,
15478            },
15479        )
15480        .expect("transition");
15481        atn.add_transition(
15482            2,
15483            ParserTransitionSpec::Atom {
15484                target: 3,
15485                label: 2,
15486            },
15487        )
15488        .expect("transition");
15489        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15490            .expect("transition");
15491        finish_atn(atn)
15492    }
15493
15494    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
15495        let mut atn = ParserAtnBuilder::new(1);
15496        for (state_number, kind) in [
15497            (0, AtnStateKind::RuleStart),
15498            (1, AtnStateKind::BlockStart),
15499            (2, AtnStateKind::Basic),
15500            (3, AtnStateKind::Basic),
15501            (4, AtnStateKind::Basic),
15502            (5, AtnStateKind::Basic),
15503            (6, AtnStateKind::BlockEnd),
15504            (7, AtnStateKind::RuleStop),
15505        ] {
15506            assert_eq!(
15507                atn.add_state(kind, Some(0)).expect("state").index(),
15508                state_number
15509            );
15510        }
15511        atn.set_rule_to_start_state(vec![0])
15512            .expect("rule start states");
15513        atn.set_rule_to_stop_state(vec![7])
15514            .expect("rule stop states");
15515        atn.add_decision_state(1).expect("decision state");
15516        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15517            .expect("transition");
15518        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15519            .expect("transition");
15520        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
15521            .expect("transition");
15522        atn.add_transition(
15523            2,
15524            ParserTransitionSpec::Predicate {
15525                target: 4,
15526                rule_index: 0,
15527                pred_index: pred_indexes[0],
15528                context_dependent: false,
15529            },
15530        )
15531        .expect("transition");
15532        atn.add_transition(
15533            3,
15534            ParserTransitionSpec::Predicate {
15535                target: 5,
15536                rule_index: 0,
15537                pred_index: pred_indexes[1],
15538                context_dependent: false,
15539            },
15540        )
15541        .expect("transition");
15542        atn.add_transition(
15543            4,
15544            ParserTransitionSpec::Atom {
15545                target: 6,
15546                label: 1,
15547            },
15548        )
15549        .expect("transition");
15550        atn.add_transition(
15551            5,
15552            ParserTransitionSpec::Atom {
15553                target: 6,
15554                label: 1,
15555            },
15556        )
15557        .expect("transition");
15558        atn.add_transition(
15559            6,
15560            ParserTransitionSpec::Atom {
15561                target: 7,
15562                label: TOKEN_EOF,
15563            },
15564        )
15565        .expect("transition");
15566        finish_atn(atn)
15567    }
15568
15569    fn nested_nullable_context_atn() -> Atn {
15570        let mut atn = ParserAtnBuilder::new(1);
15571        for state_number in 0..=20 {
15572            let kind = match state_number {
15573                0 | 10 | 16 => AtnStateKind::RuleStart,
15574                9 | 15 | 20 => AtnStateKind::RuleStop,
15575                _ => AtnStateKind::Basic,
15576            };
15577            let rule_index = match state_number {
15578                0..=9 => 0,
15579                10..=15 => 1,
15580                _ => 2,
15581            };
15582            assert_eq!(
15583                atn.add_state(kind, Some(rule_index))
15584                    .expect("state")
15585                    .index(),
15586                state_number
15587            );
15588        }
15589        atn.set_rule_to_start_state(vec![0, 10, 16])
15590            .expect("rule start states");
15591        atn.set_rule_to_stop_state(vec![9, 15, 20])
15592            .expect("rule stop states");
15593        atn.add_transition(
15594            1,
15595            ParserTransitionSpec::Rule {
15596                target: 10,
15597                rule_index: 1,
15598                follow_state: 8,
15599                precedence: 0,
15600            },
15601        )
15602        .expect("transition");
15603        atn.add_transition(
15604            8,
15605            ParserTransitionSpec::Atom {
15606                target: 9,
15607                label: 1,
15608            },
15609        )
15610        .expect("transition");
15611        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15612            .expect("transition");
15613        atn.add_transition(
15614            2,
15615            ParserTransitionSpec::Rule {
15616                target: 16,
15617                rule_index: 2,
15618                follow_state: 14,
15619                precedence: 0,
15620            },
15621        )
15622        .expect("transition");
15623        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
15624            .expect("transition");
15625        finish_atn(atn)
15626    }
15627
15628    fn generated_match_recovery_atn() -> Atn {
15629        let mut atn = ParserAtnBuilder::new(2);
15630        assert_eq!(
15631            atn.add_state(AtnStateKind::RuleStart, Some(0))
15632                .expect("state")
15633                .index(),
15634            0
15635        );
15636        assert_eq!(
15637            atn.add_state(AtnStateKind::Basic, Some(0))
15638                .expect("state")
15639                .index(),
15640            1
15641        );
15642        assert_eq!(
15643            atn.add_state(AtnStateKind::Basic, Some(0))
15644                .expect("state")
15645                .index(),
15646            2
15647        );
15648        assert_eq!(
15649            atn.add_state(AtnStateKind::RuleStop, Some(0))
15650                .expect("state")
15651                .index(),
15652            3
15653        );
15654        assert_eq!(
15655            atn.add_state(AtnStateKind::RuleStart, Some(1))
15656                .expect("state")
15657                .index(),
15658            4
15659        );
15660        assert_eq!(
15661            atn.add_state(AtnStateKind::RuleStop, Some(1))
15662                .expect("state")
15663                .index(),
15664            5
15665        );
15666        atn.set_rule_to_start_state(vec![0, 4])
15667            .expect("rule start states");
15668        atn.set_rule_to_stop_state(vec![3, 5])
15669            .expect("rule stop states");
15670        atn.add_transition(
15671            1,
15672            ParserTransitionSpec::Rule {
15673                target: 4,
15674                rule_index: 1,
15675                follow_state: 2,
15676                precedence: 0,
15677            },
15678        )
15679        .expect("transition");
15680        atn.add_transition(
15681            2,
15682            ParserTransitionSpec::Atom {
15683                target: 3,
15684                label: TOKEN_EOF,
15685            },
15686        )
15687        .expect("transition");
15688        finish_atn(atn)
15689    }
15690
15691    fn complement_set_atn() -> Atn {
15692        let mut atn = ParserAtnBuilder::new(1);
15693        assert_eq!(
15694            atn.add_state(AtnStateKind::RuleStart, Some(0))
15695                .expect("state")
15696                .index(),
15697            0
15698        );
15699        assert_eq!(
15700            atn.add_state(AtnStateKind::RuleStop, Some(0))
15701                .expect("state")
15702                .index(),
15703            1
15704        );
15705        atn.set_rule_to_start_state(vec![0])
15706            .expect("rule start states");
15707        atn.set_rule_to_stop_state(vec![1])
15708            .expect("rule stop states");
15709        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
15710        atn.add_transition(
15711            0,
15712            ParserTransitionSpec::NotSet {
15713                target: 1,
15714                set: excluded,
15715            },
15716        )
15717        .expect("transition");
15718        finish_atn(atn)
15719    }
15720
15721    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
15722    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
15723    fn wildcard_then_eof_atn() -> Atn {
15724        let mut atn = ParserAtnBuilder::new(1);
15725        assert_eq!(
15726            atn.add_state(AtnStateKind::RuleStart, Some(0))
15727                .expect("state")
15728                .index(),
15729            0
15730        );
15731        assert_eq!(
15732            atn.add_state(AtnStateKind::RuleStop, Some(0))
15733                .expect("state")
15734                .index(),
15735            1
15736        );
15737        assert_eq!(
15738            atn.add_state(AtnStateKind::Basic, Some(0))
15739                .expect("state")
15740                .index(),
15741            2
15742        );
15743        atn.set_rule_to_start_state(vec![0])
15744            .expect("rule start states");
15745        atn.set_rule_to_stop_state(vec![1])
15746            .expect("rule stop states");
15747        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
15748            .expect("transition");
15749        atn.add_transition(
15750            2,
15751            ParserTransitionSpec::Atom {
15752                target: 1,
15753                label: TOKEN_EOF,
15754            },
15755        )
15756        .expect("transition");
15757        finish_atn(atn)
15758    }
15759
15760    #[test]
15761    fn parser_matches_token_and_reports_mismatch() {
15762        let source = Source {
15763            tokens: vec![
15764                TestToken::new(1).with_text("x"),
15765                TestToken::eof("parser-test", 1, 1, 1),
15766            ],
15767            index: 0,
15768        };
15769        let data = RecognizerData::new(
15770            "Mini.g4",
15771            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15772        );
15773        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
15774        let matched = parser.match_token(1).expect("token 1 should match");
15775        assert_eq!(parser.node(matched).text(), "x");
15776        assert!(parser.match_token(1).is_err());
15777    }
15778
15779    #[test]
15780    fn parser_matches_token_sets() {
15781        let mut parser = mini_parser(vec![
15782            TestToken::new(1).with_text("x"),
15783            TestToken::eof("parser-test", 1, 1, 1),
15784        ]);
15785
15786        let matched = parser
15787            .match_set(&[(1, 1), (3, 4)])
15788            .expect("token set should match");
15789        assert_eq!(parser.node(matched).text(), "x");
15790        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
15791    }
15792
15793    #[test]
15794    fn generated_rule_api_tracks_state_and_precedence() {
15795        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15796
15797        let context = parser.enter_rule(7, 2);
15798        assert_eq!(context.rule_index(), 2);
15799        assert_eq!(parser.state(), 7);
15800        assert_eq!(
15801            parser.rule_context_stack,
15802            vec![RuleContextFrame {
15803                rule_index: 2,
15804                invoking_state: 7
15805            }]
15806        );
15807
15808        let recursive = parser.enter_recursion_rule(11, 3, 4);
15809        assert_eq!(recursive.rule_index(), 3);
15810        assert!(parser.precpred(4));
15811        assert!(parser.precpred(5));
15812        assert!(!parser.precpred(3));
15813
15814        let next = parser.push_new_recursion_context(13, 3);
15815        assert_eq!(next.invoking_state(), 13);
15816        parser.unroll_recursion_context();
15817        assert_eq!(parser.precedence_stack, vec![0]);
15818        assert_eq!(
15819            parser.rule_context_stack,
15820            vec![RuleContextFrame {
15821                rule_index: 2,
15822                invoking_state: 7
15823            }]
15824        );
15825
15826        parser.exit_rule();
15827        assert!(parser.rule_context_stack.is_empty());
15828    }
15829
15830    #[test]
15831    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
15832        let mut parser = mini_parser(vec![
15833            TestToken::new(1).with_text("x"),
15834            TestToken::eof("parser-test", 1, 1, 1),
15835        ]);
15836        let matched = parser.match_token(1).expect("token should match");
15837        assert_eq!(parser.node(matched).text(), "x");
15838        parser.record_generated_syntax_error();
15839        parser.set_int_member(7, 11);
15840        parser.set_build_parse_trees(false);
15841        parser.set_report_diagnostic_errors(true);
15842        parser.set_prediction_mode(PredictionMode::Sll);
15843        parser.set_bail_on_error(true);
15844        let _context = parser.enter_recursion_rule(9, 0, 4);
15845        parser.pending_invoking_states.push(5);
15846        parser.unknown_predicate_hits.push((0, 1));
15847        parser.unhandled_action_hits.push((0, 2));
15848
15849        parser.reset();
15850
15851        assert_eq!(parser.input.index(), 0);
15852        assert_eq!(parser.la(1), 1);
15853        assert_eq!(parser.state(), -1);
15854        assert_eq!(parser.number_of_syntax_errors(), 0);
15855        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15856        assert!(parser.rule_context_stack.is_empty());
15857        assert!(parser.pending_invoking_states.is_empty());
15858        assert_eq!(parser.precedence_stack, [0]);
15859        assert!(parser.unknown_predicate_hits.is_empty());
15860        assert!(parser.unhandled_action_hits.is_empty());
15861        assert_eq!(parser.int_member(7), Some(11));
15862        assert!(!parser.build_parse_trees());
15863        assert!(parser.report_diagnostic_errors());
15864        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
15865        assert!(parser.bail_on_error());
15866    }
15867
15868    #[test]
15869    fn set_token_stream_replaces_input_and_resets_parser() {
15870        let mut parser = mini_parser(vec![
15871            TestToken::new(1).with_text("old"),
15872            TestToken::eof("parser-test", 1, 1, 1),
15873        ]);
15874        parser.consume();
15875        parser.record_generated_syntax_error();
15876        let replacement = CommonTokenStream::new(Source {
15877            tokens: vec![
15878                TestToken::new(2).with_text("new"),
15879                TestToken::eof("parser-test", 1, 1, 1),
15880            ],
15881            index: 0,
15882        });
15883
15884        parser.set_token_stream(replacement);
15885
15886        assert_eq!(parser.input.index(), 0);
15887        assert_eq!(parser.la(1), 2);
15888        assert_eq!(parser.input.text_all(), "new");
15889        assert_eq!(parser.number_of_syntax_errors(), 0);
15890    }
15891
15892    #[test]
15893    fn active_invocation_states_exclude_the_root_frame() {
15894        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15895
15896        let _root = parser.enter_rule(0, 0);
15897        assert!(parser.active_invocation_states().is_empty());
15898
15899        let marker = parser.push_invoking_state(6);
15900        let _child = parser.enter_rule(2, 1);
15901        parser.discard_invoking_state(marker);
15902        assert_eq!(parser.active_invocation_states(), [6]);
15903
15904        let marker = parser.push_invoking_state(13);
15905        let _grandchild = parser.enter_rule(4, 2);
15906        parser.discard_invoking_state(marker);
15907        assert_eq!(parser.active_invocation_states(), [13, 6]);
15908
15909        parser.exit_rule();
15910        parser.exit_rule();
15911        parser.exit_rule();
15912    }
15913
15914    #[test]
15915    fn parser_predicates_support_token_adjacency() {
15916        let mut parser = mini_parser(vec![
15917            TestToken::new(1).with_text("=").with_span(0, 0),
15918            TestToken::new(1).with_text(">").with_span(1, 1),
15919            TestToken::eof("parser-test", 2, 1, 2),
15920        ]);
15921        parser.consume();
15922        parser.consume();
15923
15924        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
15925
15926        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15927
15928        let mut parser = mini_parser(vec![
15929            TestToken::new(1).with_text("=").with_span(0, 0),
15930            TestToken::new(1)
15931                .with_text(" ")
15932                .with_channel(HIDDEN_CHANNEL)
15933                .with_span(1, 1),
15934            TestToken::new(1).with_text(">").with_span(2, 2),
15935            TestToken::eof("parser-test", 3, 1, 3),
15936        ]);
15937        parser.consume();
15938        parser.consume();
15939
15940        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15941    }
15942
15943    #[test]
15944    fn parser_predicates_support_context_child_text_checks() {
15945        let mut parser = mini_parser(vec![
15946            TestToken::new(1).with_text("var"),
15947            TestToken::eof("parser-test", 1, 1, 1),
15948        ]);
15949        let mut context = ParserRuleContext::new(1, 0);
15950        let mut child_context = ParserRuleContext::new(2, 0);
15951        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
15952        parser.tree.add_child(&mut child_context, terminal);
15953        let child = parser.rule_node(child_context);
15954        parser.tree.add_child(&mut context, child);
15955        let predicates = [(
15956            1,
15957            0,
15958            ParserPredicate::ContextChildRuleTextNotEquals {
15959                rule_index: 2,
15960                text: "var",
15961            },
15962        )];
15963
15964        assert!(
15965            !parser.parser_semantic_predicate_matches_with_context_and_local(
15966                &predicates,
15967                1,
15968                0,
15969                &context,
15970                0,
15971            )
15972        );
15973    }
15974
15975    #[test]
15976    fn context_expected_symbols_walks_nullable_parent_contexts() {
15977        let atn = nested_nullable_context_atn();
15978        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15979        parser.rule_context_stack = vec![
15980            RuleContextFrame {
15981                rule_index: 0,
15982                invoking_state: 0,
15983            },
15984            RuleContextFrame {
15985                rule_index: 1,
15986                invoking_state: 1,
15987            },
15988            RuleContextFrame {
15989                rule_index: 2,
15990                invoking_state: 2,
15991            },
15992        ];
15993
15994        let expected = parser.context_expected_symbols(&atn);
15995
15996        assert!(expected.contains(&1));
15997        assert!(expected.contains(&TOKEN_EOF));
15998    }
15999
16000    #[test]
16001    fn prediction_context_return_states_track_rule_stack_changes() {
16002        let atn = nested_nullable_context_atn();
16003        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16004        parser.rule_context_stack = vec![
16005            RuleContextFrame {
16006                rule_index: 0,
16007                invoking_state: 0,
16008            },
16009            RuleContextFrame {
16010                rule_index: 1,
16011                invoking_state: 1,
16012            },
16013            RuleContextFrame {
16014                rule_index: 2,
16015                invoking_state: 2,
16016            },
16017        ];
16018
16019        let initial_version = parser.rule_context_version();
16020        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
16021        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
16022        assert_eq!(first, second);
16023        assert_eq!(parser.rule_context_version(), initial_version);
16024
16025        parser.exit_rule();
16026        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
16027        assert_ne!(first, after_pop);
16028        assert_ne!(parser.rule_context_version(), initial_version);
16029    }
16030
16031    #[test]
16032    fn generated_match_token_recovers_missing_token_from_context_follow() {
16033        let atn = generated_match_recovery_atn();
16034        let data = RecognizerData::new(
16035            "Mini.g4",
16036            Vocabulary::new(
16037                [None, Some("'X'"), Some("'Y'")],
16038                [None, Some("X"), Some("Y")],
16039                [None::<&str>, None, None],
16040            ),
16041        );
16042        let mut parser = BaseParser::new(
16043            CommonTokenStream::new(Source {
16044                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
16045                index: 0,
16046            }),
16047            data,
16048        );
16049        parser.rule_context_stack = vec![
16050            RuleContextFrame {
16051                rule_index: 0,
16052                invoking_state: 0,
16053            },
16054            RuleContextFrame {
16055                rule_index: 1,
16056                invoking_state: 1,
16057            },
16058        ];
16059        assert_eq!(parser.number_of_syntax_errors(), 0);
16060
16061        let node = parser
16062            .match_token_recovering(2, 5, &atn)
16063            .expect("generated match should insert missing token");
16064
16065        assert_eq!(node.children().len(), 1);
16066        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
16067        assert_eq!(
16068            node.clone()
16069                .into_child_iter()
16070                .map(|child| parser.node(child).text())
16071                .collect::<Vec<_>>(),
16072            ["<missing 'Y'>"]
16073        );
16074        // Single-token insertion synthesizes a missing token and consumes nothing,
16075        // so no EOF terminal is consumed even though lookahead is EOF.
16076        assert!(!node.consumed_eof());
16077        assert_eq!(parser.la(1), TOKEN_EOF);
16078        assert_eq!(parser.number_of_syntax_errors(), 1);
16079        assert_eq!(
16080            parser.generated_parser_diagnostics,
16081            [ParserDiagnostic {
16082                line: 1,
16083                column: 3,
16084                message: "missing 'Y' at '<EOF>'".to_owned(),
16085                offending: parser.input.lt_id(1),
16086            }]
16087        );
16088    }
16089
16090    #[test]
16091    fn generated_match_token_counts_single_token_deletion_recovery() {
16092        let atn = generated_match_recovery_atn();
16093        let data = RecognizerData::new(
16094            "Mini.g4",
16095            Vocabulary::new(
16096                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
16097                [None, Some("X"), Some("Y"), Some("Z")],
16098                [None::<&str>, None, None, None],
16099            ),
16100        );
16101        let mut parser = BaseParser::new(
16102            CommonTokenStream::new(Source {
16103                tokens: vec![
16104                    TestToken::new(3).with_text("z"),
16105                    TestToken::new(2).with_text("y"),
16106                    TestToken::eof("parser-test", 3, 1, 3),
16107                ],
16108                index: 0,
16109            }),
16110            data,
16111        );
16112
16113        let node = parser
16114            .match_token_recovering(2, 5, &atn)
16115            .expect("generated match should delete the extraneous token");
16116
16117        assert_eq!(node.children().len(), 2);
16118        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
16119        assert_eq!(parser.node(node.children()[0]).text(), "z");
16120        assert_eq!(parser.node(node.children()[1]).text(), "y");
16121        assert_eq!(
16122            node.into_child_iter()
16123                .map(|child| parser.node(child).text())
16124                .collect::<Vec<_>>(),
16125            ["z", "y"]
16126        );
16127        assert_eq!(parser.number_of_syntax_errors(), 1);
16128    }
16129
16130    #[test]
16131    fn generated_match_token_iterates_single_success_without_a_children_vec() {
16132        let atn = generated_match_recovery_atn();
16133        let data = RecognizerData::new(
16134            "Mini.g4",
16135            Vocabulary::new(
16136                [None, Some("'X'"), Some("'Y'")],
16137                [None, Some("X"), Some("Y")],
16138                [None::<&str>, None, None],
16139            ),
16140        );
16141        let mut parser = BaseParser::new(
16142            CommonTokenStream::new(Source {
16143                tokens: vec![
16144                    TestToken::new(2).with_text("y"),
16145                    TestToken::eof("parser-test", 1, 1, 1),
16146                ],
16147                index: 0,
16148            }),
16149            data,
16150        );
16151
16152        let node = parser
16153            .match_token_recovering(2, 5, &atn)
16154            .expect("generated match should consume the expected token");
16155
16156        assert_eq!(
16157            node.into_child_iter()
16158                .map(|child| parser.node(child).text())
16159                .collect::<Vec<_>>(),
16160            ["y"]
16161        );
16162        assert_eq!(parser.number_of_syntax_errors(), 0);
16163    }
16164
16165    #[test]
16166    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
16167        let atn = generated_match_recovery_atn();
16168        let data = RecognizerData::new(
16169            "Mini.g4",
16170            Vocabulary::new(
16171                [None, Some("'X'"), Some("'Y'")],
16172                [None, Some("X"), Some("Y")],
16173                [None::<&str>, None, None],
16174            ),
16175        );
16176        let mut parser = BaseParser::new(
16177            CommonTokenStream::new(Source {
16178                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
16179                index: 0,
16180            }),
16181            data,
16182        );
16183        parser.rule_context_stack = vec![
16184            RuleContextFrame {
16185                rule_index: 0,
16186                invoking_state: 0,
16187            },
16188            RuleContextFrame {
16189                rule_index: 1,
16190                invoking_state: 1,
16191            },
16192        ];
16193        let marker = parser.generated_diagnostics_checkpoint();
16194
16195        let _ = parser
16196            .match_token_recovering(2, 5, &atn)
16197            .expect("generated match should insert missing token");
16198        assert_eq!(parser.number_of_syntax_errors(), 1);
16199
16200        parser.restore_generated_diagnostics(marker);
16201
16202        assert_eq!(parser.number_of_syntax_errors(), 0);
16203        assert!(parser.generated_parser_diagnostics.is_empty());
16204    }
16205
16206    #[test]
16207    fn generated_prediction_diagnostics_use_adaptive_context() {
16208        let atn = two_alt_decision_atn();
16209        let data = RecognizerData::new(
16210            "Mini.g4",
16211            Vocabulary::new(
16212                [None, Some("'x'"), Some("'y'")],
16213                [None, Some("X"), Some("Y")],
16214                [None::<&str>, None, None],
16215            ),
16216        )
16217        .with_rule_names(["s"]);
16218        let mut parser = BaseParser::new(
16219            CommonTokenStream::new(Source {
16220                tokens: vec![
16221                    TestToken::new(1)
16222                        .with_text("x")
16223                        .with_position(1, 0)
16224                        .with_span(0, 0),
16225                    TestToken::new(2)
16226                        .with_text("y")
16227                        .with_position(1, 2)
16228                        .with_span(1, 1),
16229                    TestToken::eof("parser-test", 2, 1, 3),
16230                ],
16231                index: 0,
16232            }),
16233            data,
16234        );
16235        parser.set_report_diagnostic_errors(true);
16236
16237        parser.record_generated_prediction_diagnostic(
16238            &atn,
16239            1,
16240            &ParserAtnPrediction {
16241                alt: 1,
16242                requires_full_context: true,
16243                has_semantic_context: false,
16244                diagnostic: Some(ParserAtnPredictionDiagnostic {
16245                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
16246                    start_index: 0,
16247                    sll_stop_index: 1,
16248                    ll_stop_index: 0,
16249                    conflicting_alts: vec![1, 2],
16250                    exact: false,
16251                }),
16252            },
16253        );
16254        // Ambiguities from the default LL prediction mode are non-exact, so —
16255        // matching Java's exactOnly DiagnosticErrorListener — only the
16256        // attempting-full-context line is reported. Exact-ambiguity mode
16257        // reports the ambiguity itself.
16258        parser.record_generated_prediction_diagnostic(
16259            &atn,
16260            1,
16261            &ParserAtnPrediction {
16262                alt: 1,
16263                requires_full_context: true,
16264                has_semantic_context: false,
16265                diagnostic: Some(ParserAtnPredictionDiagnostic {
16266                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
16267                    start_index: 0,
16268                    sll_stop_index: 1,
16269                    ll_stop_index: 1,
16270                    conflicting_alts: vec![1, 2],
16271                    exact: false,
16272                }),
16273            },
16274        );
16275
16276        // The full-context/context-sensitivity diagnostic trace (order + decision + input windows)
16277        // is one snapshot rather than three ParserDiagnostic literals.
16278        insta::assert_debug_snapshot!(
16279            "generated_prediction_diagnostics_use_adaptive_context",
16280            parser.generated_parser_diagnostics
16281        );
16282    }
16283
16284    #[test]
16285    fn generated_match_not_set_recovers_empty_complement_at_eof() {
16286        let atn = complement_set_atn();
16287        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16288        parser.rule_context_stack = vec![RuleContextFrame {
16289            rule_index: 0,
16290            invoking_state: 0,
16291        }];
16292
16293        let node = parser
16294            .match_not_token_set_recovering(
16295                atn.token_set(0).expect("excluded token set"),
16296                1,
16297                1,
16298                1,
16299                &atn,
16300            )
16301            .expect("empty complement should recover at EOF");
16302
16303        assert_eq!(node.children().len(), 1);
16304        // Recovery synthesizes a missing token without consuming EOF, so the
16305        // enclosing rule must not record EOF as its stop token.
16306        assert!(!node.consumed_eof());
16307        assert_eq!(parser.la(1), TOKEN_EOF);
16308        assert_eq!(
16309            parser.generated_parser_diagnostics,
16310            [ParserDiagnostic {
16311                line: 1,
16312                column: 1,
16313                message: "missing {} at '<EOF>'".to_owned(),
16314                offending: parser.input.lt_id(1),
16315            }]
16316        );
16317    }
16318
16319    #[test]
16320    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
16321        // `start : . EOF ;` on empty input. The wildcard is modeled as an
16322        // empty-complement not-set; at EOF the follow state (the explicit EOF
16323        // match) expects EOF, so even in the start rule recovery must perform
16324        // single-token insertion (`<missing ...>`) rather than aborting — matching
16325        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
16326        let atn = wildcard_then_eof_atn();
16327        let data = RecognizerData::new(
16328            "Mini.g4",
16329            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16330        );
16331        let mut parser = BaseParser::new(
16332            CommonTokenStream::new(Source {
16333                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
16334                index: 0,
16335            }),
16336            data,
16337        );
16338        parser.rule_context_stack = vec![RuleContextFrame {
16339            rule_index: 0,
16340            invoking_state: 0,
16341        }];
16342
16343        let node = parser
16344            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
16345            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
16346
16347        // A single `<missing ...>` error node is inserted; EOF is not consumed.
16348        assert_eq!(node.children().len(), 1);
16349        assert!(!node.consumed_eof());
16350        assert!(
16351            parser
16352                .node(node.children()[0])
16353                .text()
16354                .starts_with("<missing")
16355        );
16356        assert_eq!(parser.la(1), TOKEN_EOF);
16357        assert_eq!(
16358            parser.generated_parser_diagnostics,
16359            [ParserDiagnostic {
16360                line: 1,
16361                column: 1,
16362                message: "missing 'x' at '<EOF>'".to_owned(),
16363                offending: parser.input.lt_id(1),
16364            }]
16365        );
16366    }
16367
16368    #[test]
16369    fn generated_rule_recovery_consumes_to_parent_follow() {
16370        let atn = generated_match_recovery_atn();
16371        let data = RecognizerData::new(
16372            "Mini.g4",
16373            Vocabulary::new(
16374                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
16375                [None, Some("X"), Some("Y"), Some("Z")],
16376                [None::<&str>, None, None, None],
16377            ),
16378        );
16379        let mut parser = BaseParser::new(
16380            CommonTokenStream::new(Source {
16381                tokens: vec![
16382                    TestToken::new(3).with_text("z"),
16383                    TestToken::eof("parser-test", 1, 1, 1),
16384                ],
16385                index: 0,
16386            }),
16387            data,
16388        );
16389        let _parent = parser.enter_rule(0, 0);
16390        let marker = parser.push_invoking_state(1);
16391        let mut child = parser.enter_rule(4, 1);
16392        parser.discard_invoking_state(marker);
16393
16394        // The anchor recorded where the error was built must survive into the
16395        // dispatched diagnostic even though recovery consumes past it below.
16396        let offending = parser.input.lt_id(1);
16397        assert!(offending.is_some(), "the 'z' token should be buffered");
16398        parser.recover_generated_rule(
16399            &mut child,
16400            &atn,
16401            AntlrError::ParserError {
16402                line: 1,
16403                column: 0,
16404                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
16405                offending,
16406            },
16407        );
16408        let tree = parser.finish_rule(child, false);
16409
16410        assert_eq!(parser.la(1), TOKEN_EOF);
16411        assert_eq!(
16412            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
16413            "(a z)"
16414        );
16415        assert_eq!(parser.number_of_syntax_errors(), 1);
16416        assert_eq!(
16417            parser.generated_parser_diagnostics,
16418            [ParserDiagnostic {
16419                line: 1,
16420                column: 0,
16421                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
16422                offending,
16423            }]
16424        );
16425        parser.exit_rule();
16426    }
16427
16428    #[test]
16429    fn generated_rule_recovery_forces_progress_after_repeated_error_state() {
16430        let atn = nested_nullable_context_atn();
16431        let mut parser = mini_parser(vec![
16432            TestToken::new(1).with_text("x"),
16433            TestToken::eof("parser-test", 1, 1, 1),
16434        ]);
16435        parser.rule_context_stack = vec![
16436            RuleContextFrame {
16437                rule_index: 0,
16438                invoking_state: 0,
16439            },
16440            RuleContextFrame {
16441                rule_index: 1,
16442                invoking_state: 1,
16443            },
16444            RuleContextFrame {
16445                rule_index: 2,
16446                invoking_state: 2,
16447            },
16448        ];
16449        parser.set_state(20);
16450        let mut context = ParserRuleContext::new(2, 2);
16451
16452        parser.recover_generated_rule(
16453            &mut context,
16454            &atn,
16455            AntlrError::NoViableAlternative {
16456                input: "'x'".to_owned(),
16457            },
16458        );
16459        assert_eq!(parser.input.index(), 0);
16460
16461        parser.set_state(21);
16462        parser.recover_generated_rule(
16463            &mut context,
16464            &atn,
16465            AntlrError::NoViableAlternative {
16466                input: "'x'".to_owned(),
16467            },
16468        );
16469        assert_eq!(parser.input.index(), 0);
16470        assert_eq!(
16471            parser.generated_recovery_error_states,
16472            BTreeSet::from([20, 21])
16473        );
16474
16475        parser.set_state(20);
16476        parser.recover_generated_rule(
16477            &mut context,
16478            &atn,
16479            AntlrError::NoViableAlternative {
16480                input: "'x'".to_owned(),
16481            },
16482        );
16483
16484        assert_eq!(parser.input.index(), 1);
16485        assert_eq!(parser.la(1), TOKEN_EOF);
16486        assert!(context.has_matched_child());
16487        assert_eq!(parser.generated_recovery_error_states, BTreeSet::from([20]));
16488
16489        parser.match_eof().expect("EOF should match");
16490        assert_eq!(parser.generated_recovery_error_index, None);
16491        assert!(parser.generated_recovery_error_states.is_empty());
16492    }
16493
16494    #[test]
16495    fn greedy_ll1_alt_handles_nullable_loop_exit() {
16496        let mut body_symbols = TokenBitSet::default();
16497        body_symbols.insert(1);
16498        let entry = DecisionLookahead {
16499            transitions: vec![
16500                TransitionLookSet {
16501                    symbols: body_symbols,
16502                    nullable: false,
16503                },
16504                TransitionLookSet {
16505                    symbols: TokenBitSet::default(),
16506                    nullable: true,
16507                },
16508            ],
16509        };
16510
16511        assert_eq!(ll1_unique_alt(&entry, 2), None);
16512        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
16513        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
16514        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
16515    }
16516
16517    #[test]
16518    fn ordinary_repetition_builds_tree_in_input_order() {
16519        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16520            let mut parser = mini_parser(repeated_x_tokens(3));
16521            let tree = parser
16522                .parse_atn_rule(&atn, 0)
16523                .expect("ordinary repetition should parse");
16524
16525            let root = parser
16526                .node(tree)
16527                .as_rule()
16528                .expect("entry result should be a rule");
16529            let body_rules = root.child_rules(1).collect::<Vec<_>>();
16530            assert_eq!(root.text(), "xxx<EOF>");
16531            assert_eq!(body_rules.len(), 3);
16532            assert_eq!(
16533                body_rules
16534                    .iter()
16535                    .map(|rule| rule.start_id().expect("body start").index())
16536                    .collect::<Vec<_>>(),
16537                [0, 1, 2]
16538            );
16539            assert_eq!(
16540                body_rules
16541                    .iter()
16542                    .map(|rule| rule.stop_id().expect("body stop").index())
16543                    .collect::<Vec<_>>(),
16544                [0, 1, 2]
16545            );
16546            assert_eq!(parser.number_of_syntax_errors(), 0);
16547        }
16548    }
16549
16550    #[test]
16551    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
16552        const DEPTH: usize = 20_000;
16553
16554        std::thread::Builder::new()
16555            .name("deferred-rule-materialization".to_owned())
16556            .stack_size(256 * 1024)
16557            .spawn(|| {
16558                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16559                let mut root = FastDeferredNodeId::EMPTY;
16560                for depth in 0..DEPTH {
16561                    root = parser
16562                        .recognition_arena
16563                        .deferred_rule_node(FastDeferredRule {
16564                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
16565                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
16566                            start_index: 0,
16567                            stop_index: None,
16568                            deferred_children: root,
16569                            children: NodeSeqId::EMPTY,
16570                        });
16571                }
16572
16573                let (mut children, alt_number) =
16574                    parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
16575                assert_eq!(alt_number, 0);
16576                for expected_rule in (0..DEPTH).rev() {
16577                    let mut nodes = parser.recognition_arena.iter(children);
16578                    let node = nodes.next().expect("nested rule node");
16579                    assert!(nodes.next().is_none(), "each rule has one child");
16580                    let ArenaRecognizedNode::Rule {
16581                        rule_index,
16582                        children: nested,
16583                        ..
16584                    } = parser.recognition_arena.node(node)
16585                    else {
16586                        panic!("expected nested rule");
16587                    };
16588                    assert_eq!(rule_index as usize, expected_rule);
16589                    children = nested;
16590                }
16591                assert!(children.is_empty());
16592            })
16593            .expect("small-stack thread should start")
16594            .join()
16595            .expect("deferred rules should materialize without recursion");
16596    }
16597
16598    #[test]
16599    fn deferred_alternatives_preserve_left_recursive_contexts() {
16600        let mut parser = mini_parser(vec![
16601            TestToken::new(1).with_text("1"),
16602            TestToken::new(2).with_text("+"),
16603            TestToken::new(1).with_text("2"),
16604            TestToken::eof("parser-test", 3, 1, 3),
16605        ]);
16606        let base = parser.arena_token_node(0, false);
16607        let operator = parser.arena_token_node(1, false);
16608        let right = parser.arena_token_node(2, false);
16609
16610        let base = parser.recognition_arena.prepend(NodeSeqId::EMPTY, base);
16611        let base = parser.recognition_arena.deferred_fragment(base);
16612        let operator = parser.recognition_arena.prepend(NodeSeqId::EMPTY, operator);
16613        let operator = parser.recognition_arena.deferred_fragment(operator);
16614        let right = parser.recognition_arena.prepend(NodeSeqId::EMPTY, right);
16615        let right = parser.recognition_arena.deferred_fragment(right);
16616        let base_alt = parser.recognition_arena.deferred_alternative(1);
16617        let boundary = parser.recognition_arena.deferred_left_recursive_boundary(0);
16618        let operator_alt = parser.recognition_arena.deferred_alternative(6);
16619
16620        let mut deferred = FastDeferredNodeId::EMPTY;
16621        for fragment in [base_alt, base, boundary, operator_alt, operator, right] {
16622            deferred = parser
16623                .recognition_arena
16624                .concat_deferred_nodes(deferred, fragment);
16625        }
16626        let (nodes, root_alt_number) =
16627            parser.materialize_fast_deferred_nodes(deferred, NodeSeqId::EMPTY);
16628        let nodes = parser
16629            .recognition_arena
16630            .fold_left_recursive_boundaries(nodes);
16631
16632        let mut root = ParserRuleContext::new(0, -1);
16633        root.set_context_alt_number(root_alt_number);
16634        let mut cursor = nodes;
16635        while let Some(link) = parser.recognition_arena.link(cursor) {
16636            let child = parser
16637                .arena_recognized_node_tree(link.head, false, true)
16638                .expect("materialized child should become a public tree");
16639            parser.tree.add_child(&mut root, child);
16640            cursor = link.tail;
16641        }
16642        let tree = parser.rule_node(root);
16643        let contexts = parser
16644            .node(tree)
16645            .descendants()
16646            .filter_map(Node::as_rule)
16647            .map(|rule| {
16648                (
16649                    rule.rule_index(),
16650                    rule.alt_number(),
16651                    rule.context_alt_number(),
16652                    rule.text(),
16653                )
16654            })
16655            .collect::<Vec<_>>();
16656
16657        insta::assert_debug_snapshot!(
16658            "deferred_alternatives_preserve_left_recursive_contexts",
16659            contexts
16660        );
16661    }
16662
16663    #[test]
16664    fn fast_recognizer_preserves_labeled_left_recursive_operator_context() {
16665        let atn = labeled_left_recursive_operator_atn();
16666        let mut parser = mini_parser(vec![
16667            TestToken::new(1).with_text("a"),
16668            TestToken::new(3).with_text("+"),
16669            TestToken::new(1).with_text("b"),
16670            TestToken::eof("parser-test", 3, 1, 3),
16671        ]);
16672
16673        let (tree, _) = parser
16674            .parse_atn_rule_with_runtime_options(
16675                &atn,
16676                0,
16677                ParserRuntimeOptions {
16678                    track_context_alt_numbers: true,
16679                    ..ParserRuntimeOptions::default()
16680                },
16681            )
16682            .expect("labeled left-recursive addition should parse");
16683        let contexts = parser
16684            .node(tree)
16685            .descendants()
16686            .filter_map(Node::as_rule)
16687            .map(|rule| {
16688                let operator = rule
16689                    .children()
16690                    .next()
16691                    .and_then(Node::as_rule)
16692                    .is_some_and(|child| child.rule_index() == rule.rule_index());
16693                (operator, rule.context_alt_number(), rule.text())
16694            })
16695            .collect::<Vec<_>>();
16696
16697        insta::assert_debug_snapshot!(
16698            "fast_recognizer_preserves_labeled_left_recursive_operator_context",
16699            contexts
16700        );
16701        assert!(!parser.recognition_arena.deferred_nodes.is_empty());
16702        assert_eq!(parser.number_of_syntax_errors(), 0);
16703    }
16704
16705    #[test]
16706    fn deeply_nested_rule_calls_grow_the_stack() {
16707        const DEPTH: usize = 4_096;
16708        const STACK_SIZE: usize = 256 * 1024;
16709        let atn = nested_rule_chain_atn(DEPTH);
16710        std::thread::Builder::new()
16711            .name("nested-adaptive-set-rules".to_owned())
16712            .stack_size(STACK_SIZE)
16713            .spawn(move || {
16714                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16715                parser.set_build_parse_trees(false);
16716                // This test isolates recognizer depth from the separately
16717                // cached FIRST-set metadata walk.
16718                parser.fast_first_set_prefilter = false;
16719                parser
16720                    .parse_atn_rule(&atn, 0)
16721                    .expect("nested rule chain should grow the native stack");
16722                assert_eq!(parser.input.index(), 1);
16723            })
16724            .expect("small-stack thread should start")
16725            .join()
16726            .expect("nested rule chain should not overflow its stack");
16727    }
16728
16729    #[test]
16730    fn deeply_nested_branching_rules_grow_the_stack() {
16731        const DEPTH: usize = 4_096;
16732        const STACK_SIZE: usize = 256 * 1024;
16733        let atn = nested_rule_graph_atn(DEPTH, true, false);
16734        std::thread::Builder::new()
16735            .name("nested-branching-rules".to_owned())
16736            .stack_size(STACK_SIZE)
16737            .spawn(move || {
16738                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16739                parser.set_build_parse_trees(false);
16740                parser
16741                    .parse_atn_rule(&atn, 0)
16742                    .expect("branching rule chain should grow the native stack");
16743                assert_eq!(parser.input.index(), 1);
16744            })
16745            .expect("small-stack thread should start")
16746            .join()
16747            .expect("branching rule chain should not overflow its stack");
16748    }
16749
16750    #[test]
16751    fn deeply_nested_rule_follows_grow_the_stack() {
16752        const DEPTH: usize = 4_096;
16753        const STACK_SIZE: usize = 256 * 1024;
16754        let atn = nested_rule_graph_atn(DEPTH, false, true);
16755        std::thread::Builder::new()
16756            .name("nested-rule-follows".to_owned())
16757            .stack_size(STACK_SIZE)
16758            .spawn(move || {
16759                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
16760                parser.set_build_parse_trees(false);
16761                parser.fast_first_set_prefilter = false;
16762                parser
16763                    .parse_atn_rule(&atn, 0)
16764                    .expect("rule follow chain should grow the native stack");
16765                assert_eq!(parser.input.index(), DEPTH);
16766            })
16767            .expect("small-stack thread should start")
16768            .join()
16769            .expect("nested rule follow chain should not overflow its stack");
16770    }
16771
16772    #[test]
16773    fn deeply_nested_recovery_grows_the_stack() {
16774        const DEPTH: usize = 4_096;
16775        const STACK_SIZE: usize = 256 * 1024;
16776        let atn = nested_rule_chain_atn(DEPTH);
16777        std::thread::Builder::new()
16778            .name("nested-rule-recovery".to_owned())
16779            .stack_size(STACK_SIZE)
16780            .spawn(move || {
16781                let mut parser = mini_parser(vec![
16782                    TestToken::new(2).with_text("z"),
16783                    TestToken::new(1).with_text("x"),
16784                    TestToken::eof("parser-test", 2, 1, 2),
16785                ]);
16786                parser.set_build_parse_trees(false);
16787                parser.fast_first_set_prefilter = false;
16788                parser
16789                    .parse_atn_rule(&atn, 0)
16790                    .expect("nested recovery should grow the native stack");
16791                assert_eq!(parser.input.index(), 2);
16792                assert_eq!(parser.number_of_syntax_errors(), 1);
16793            })
16794            .expect("small-stack thread should start")
16795            .join()
16796            .expect("nested rule recovery should not overflow its stack");
16797    }
16798
16799    #[test]
16800    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
16801        const REPETITIONS: usize = 64;
16802
16803        let atn = ambiguous_ordinary_star_loop_atn();
16804        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16805        let tree = parser
16806            .parse_atn_rule(&atn, 0)
16807            .expect("ambiguous ordinary repetition should parse");
16808
16809        let root = parser
16810            .node(tree)
16811            .as_rule()
16812            .expect("entry result should be a rule");
16813        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
16814        assert_eq!(parser.input.index(), REPETITIONS);
16815        assert!(
16816            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
16817            "equivalent segmentations should keep deferred storage linear"
16818        );
16819        assert_eq!(parser.number_of_syntax_errors(), 0);
16820    }
16821
16822    #[test]
16823    fn long_ordinary_repetition_does_not_consume_native_stack() {
16824        const REPETITIONS: usize = 20_000;
16825
16826        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16827            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16828            parser.set_build_parse_trees(false);
16829            parser
16830                .parse_atn_rule(&atn, 0)
16831                .expect("long ordinary repetition should parse");
16832
16833            assert_eq!(parser.input.index(), REPETITIONS);
16834            assert_eq!(parser.number_of_syntax_errors(), 0);
16835        }
16836    }
16837
16838    #[test]
16839    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
16840        const REPETITIONS: usize = 2_000;
16841        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
16842
16843        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16844            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16845            let tree = parser
16846                .parse_atn_rule(&atn, 0)
16847                .expect("long rule repetition should parse");
16848
16849            let root = parser
16850                .node(tree)
16851                .as_rule()
16852                .expect("entry result should be a rule");
16853            assert_eq!(root.text(), expected_text);
16854            assert_eq!(root.child_rules(1).count(), REPETITIONS);
16855            let first_body = root.child_rules(1).next().expect("first body rule");
16856            let last_body = root.child_rules(1).next_back().expect("last body rule");
16857            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
16858            assert_eq!(
16859                last_body.stop_id().expect("last body stop").index(),
16860                REPETITIONS - 1
16861            );
16862
16863            let stats = parser.recognition_arena_stats();
16864            assert_eq!(
16865                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16866                (REPETITIONS, REPETITIONS, 0)
16867            );
16868            assert_eq!(
16869                (stats.total_links, stats.live_links, stats.dead_links),
16870                (REPETITIONS, REPETITIONS, 0)
16871            );
16872            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
16873            assert_eq!(
16874                parser.recognition_arena.deferred_nodes.len(),
16875                REPETITIONS * 2 - 1
16876            );
16877            assert_eq!(parser.number_of_syntax_errors(), 0);
16878        }
16879    }
16880
16881    #[test]
16882    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
16883        let key = |state_number| FastRecognizeKey {
16884            state_number,
16885            stop_state: 10,
16886            index: state_number,
16887            rule_start_index: 0,
16888            decision_start_index: None,
16889            precedence: 0,
16890            recovery_symbols_id: 0,
16891            recovery_state: None,
16892        };
16893
16894        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16895        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
16896            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
16897        }
16898        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
16899        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
16900
16901        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16902        let repeated = key(1);
16903        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
16904            assert!(promote.clean_memo_enabled_for_key(&repeated));
16905        }
16906        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
16907
16908        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
16909            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
16910        }
16911        assert!(sparse.clean_memo_enabled_for_key(&repeated));
16912        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
16913        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
16914            assert!(sparse.clean_memo_enabled_for_key(&repeated));
16915        }
16916        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
16917    }
16918
16919    #[test]
16920    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
16921        assert_eq!(
16922            fast_recognize_memo_capacity(0),
16923            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16924        );
16925        assert_eq!(
16926            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
16927            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16928        );
16929        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
16930        assert_eq!(
16931            fast_recognize_memo_capacity(usize::MAX),
16932            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
16933        );
16934    }
16935
16936    #[test]
16937    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
16938        let mut scratch = FastRecognizeTopScratch::default();
16939        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16940        let retained_capacity = scratch.memo.capacity();
16941        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16942        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16943
16944        let larger_capacity = retained_capacity + 1;
16945        scratch.prepare(larger_capacity);
16946        let grown_capacity = scratch.memo.capacity();
16947        assert!(grown_capacity >= larger_capacity);
16948        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16949
16950        scratch.memo.insert(
16951            FastRecognizeKey {
16952                state_number: 0,
16953                stop_state: 0,
16954                index: 0,
16955                rule_start_index: 0,
16956                decision_start_index: None,
16957                precedence: 0,
16958                recovery_symbols_id: 0,
16959                recovery_state: None,
16960            },
16961            Rc::from([FastRecognizeOutcome {
16962                index: 0,
16963                consumed_eof: false,
16964                diagnostics: DiagnosticSeqId::EMPTY,
16965                deferred_nodes: FastDeferredNodeId::EMPTY,
16966                nodes: NodeSeqId::EMPTY,
16967            }]),
16968        );
16969        scratch.release_oversized_memo();
16970        assert!(scratch.memo.is_empty());
16971        assert_eq!(scratch.memo.capacity(), grown_capacity);
16972
16973        scratch
16974            .memo
16975            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
16976        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16977
16978        scratch.release_oversized_memo();
16979        assert!(scratch.memo.is_empty());
16980        assert_eq!(scratch.memo.capacity(), 0);
16981    }
16982
16983    #[test]
16984    fn clean_empty_multi_alt_outcomes_are_memoized() {
16985        let mut atn = ParserAtnBuilder::new(2);
16986        assert_eq!(
16987            atn.add_state(AtnStateKind::RuleStart, Some(0))
16988                .expect("state")
16989                .index(),
16990            0
16991        );
16992        assert_eq!(
16993            atn.add_state(AtnStateKind::BlockStart, Some(0))
16994                .expect("state")
16995                .index(),
16996            1
16997        );
16998        assert_eq!(
16999            atn.add_state(AtnStateKind::RuleStop, Some(0))
17000                .expect("state")
17001                .index(),
17002            2
17003        );
17004        atn.set_rule_to_start_state(vec![0])
17005            .expect("rule start states");
17006        atn.set_rule_to_stop_state(vec![2])
17007            .expect("rule stop states");
17008        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17009            .expect("transition");
17010        atn.add_transition(
17011            1,
17012            ParserTransitionSpec::Atom {
17013                target: 2,
17014                label: 1,
17015            },
17016        )
17017        .expect("transition");
17018        atn.add_transition(
17019            1,
17020            ParserTransitionSpec::Atom {
17021                target: 2,
17022                label: 2,
17023            },
17024        )
17025        .expect("transition");
17026        let atn = finish_atn(atn);
17027
17028        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
17029        parser.fast_recovery_enabled = false;
17030        let mut visiting = FxHashSet::default();
17031        let mut memo = FxHashMap::default();
17032        let mut expected = ExpectedTokens::default();
17033        let outcomes = parser.recognize_state_fast(
17034            &atn,
17035            FastRecognizeRequest {
17036                state_number: 1,
17037                stop_state: 2,
17038                index: 0,
17039                rule_start_index: 0,
17040                decision_start_index: None,
17041                precedence: 0,
17042                depth: 0,
17043                recovery_symbols: parser.empty_recovery_symbols(),
17044                recovery_state: None,
17045            },
17046            FastRecognizeScratch {
17047                predicate_context: None,
17048                visiting: &mut visiting,
17049                memo: &mut memo,
17050                expected: &mut expected,
17051                native_depth: 0,
17052            },
17053        );
17054
17055        assert!(outcomes.is_empty());
17056        assert_eq!(memo.len(), 1);
17057        assert!(memo.values().next().expect("memo entry").is_empty());
17058
17059        parser.clean_memo_mode = CleanMemoMode::Sparse;
17060        visiting.clear();
17061        memo.clear();
17062        expected = ExpectedTokens::default();
17063        let sparse_outcomes = parser.recognize_state_fast(
17064            &atn,
17065            FastRecognizeRequest {
17066                state_number: 1,
17067                stop_state: 2,
17068                index: 0,
17069                rule_start_index: 0,
17070                decision_start_index: None,
17071                precedence: 0,
17072                depth: 0,
17073                recovery_symbols: parser.empty_recovery_symbols(),
17074                recovery_state: None,
17075            },
17076            FastRecognizeScratch {
17077                predicate_context: None,
17078                visiting: &mut visiting,
17079                memo: &mut memo,
17080                expected: &mut expected,
17081                native_depth: 0,
17082            },
17083        );
17084
17085        assert!(sparse_outcomes.is_empty());
17086        assert!(memo.is_empty());
17087    }
17088
17089    #[test]
17090    fn wildcard_matches_non_eof_only() {
17091        let mut parser = mini_parser(vec![
17092            TestToken::new(1).with_text("x"),
17093            TestToken::eof("parser-test", 1, 1, 1),
17094        ]);
17095        let matched = parser.match_wildcard().expect("wildcard");
17096        assert_eq!(parser.node(matched).text(), "x");
17097        assert!(parser.match_wildcard().is_err());
17098    }
17099
17100    #[test]
17101    fn add_parse_child_records_match_even_without_tree_building() {
17102        // `sync_decision`'s "is the current context empty" flag must reflect real
17103        // matches, not parse-tree children: when `build_parse_trees(false)`,
17104        // `children` stays empty but `has_matched_child` must still flip so nested
17105        // recovery does not wrongly suppress single-token deletion.
17106        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
17107        let token = TestToken::new(1).with_text("x");
17108
17109        parser.set_build_parse_trees(false);
17110        let mut ctx = ParserRuleContext::new(0, 0);
17111        assert!(!ctx.has_matched_child());
17112        let child = parser.terminal_tree(token.id);
17113        parser.add_parse_child(&mut ctx, child);
17114        // Tree building is off, so no child is stored...
17115        assert_eq!(ctx.child_count(), 0);
17116        assert_eq!(parser.parse_tree_storage().node_count(), 0);
17117        // ...but the match is recorded, so the context is no longer "empty".
17118        assert!(ctx.has_matched_child());
17119
17120        // With tree building on, the child is stored and the match is recorded.
17121        parser.set_build_parse_trees(true);
17122        let mut ctx = ParserRuleContext::new(0, 0);
17123        let child = parser.terminal_tree(token.id);
17124        parser.add_parse_child(&mut ctx, child);
17125        assert_eq!(ctx.child_count(), 1);
17126        assert!(ctx.has_matched_child());
17127    }
17128
17129    #[test]
17130    fn disabled_tree_building_does_not_grow_flat_storage() {
17131        let mut parser = mini_parser(vec![
17132            TestToken::new(1).with_text("x"),
17133            TestToken::new(1).with_text("y"),
17134            TestToken::eof("parser-test", 2, 1, 2),
17135        ]);
17136        parser.set_build_parse_trees(false);
17137        let mut context = ParserRuleContext::new(0, -1);
17138
17139        for _ in 0..2 {
17140            let child = parser.match_token(1).expect("token should match");
17141            parser.add_parse_child(&mut context, child);
17142        }
17143        let current = parser.input.lt_id(1).expect("EOF token");
17144        let error = parser.error_tree(current);
17145        parser.add_parse_child(&mut context, error);
17146        let root = parser.rule_node(context);
17147
17148        assert_eq!(
17149            parser.parse_tree_storage().stats(),
17150            ParseTreeStats::default()
17151        );
17152        assert!(
17153            parser
17154                .parse_tree_storage()
17155                .node(parser.token_store(), root)
17156                .is_none(),
17157            "the no-tree sentinel must not resolve to stored data"
17158        );
17159    }
17160
17161    #[test]
17162    fn disabled_tree_building_skips_recognition_rule_node_storage() {
17163        let atn = ordinary_star_loop_atn();
17164        let mut parser = mini_parser(repeated_x_tokens(3));
17165        parser.set_build_parse_trees(false);
17166
17167        parser
17168            .parse_atn_rule(&atn, 0)
17169            .expect("ordinary repetition should parse without a tree");
17170
17171        assert_eq!(parser.input.index(), 3);
17172        assert!(parser.recognition_arena.nodes.is_empty());
17173        assert!(parser.recognition_arena.seq_links.is_empty());
17174        assert!(parser.recognition_arena.deferred_nodes.is_empty());
17175        assert!(parser.recognition_arena.deferred_rules.is_empty());
17176        assert!(!parser.fast_token_nodes_enabled);
17177        assert!(parser.fast_recognize_scratch.memo.is_empty());
17178    }
17179
17180    #[test]
17181    fn parser_interprets_simple_atn_rule() {
17182        let atn = token_then_eof_atn();
17183        let mut parser = mini_parser(vec![
17184            TestToken::new(1).with_text("x"),
17185            TestToken::eof("parser-test", 1, 1, 1),
17186        ]);
17187
17188        let tree = parser
17189            .parse_atn_rule(&atn, 0)
17190            .expect("artificial parser rule should parse");
17191        assert_eq!(parser.node(tree).text(), "x<EOF>");
17192        assert_eq!(parser.number_of_syntax_errors(), 0);
17193        assert_eq!(
17194            parser
17195                .node(tree)
17196                .first_rule_stop(0)
17197                .expect("rule should stop at EOF")
17198                .token_type(),
17199            TOKEN_EOF
17200        );
17201
17202        let mut parser = mini_parser(vec![
17203            TestToken::new(1).with_text("x"),
17204            TestToken::eof("parser-test", 1, 1, 1),
17205        ]);
17206        let (tree, actions) = parser
17207            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17208            .expect("runtime-option parser rule should parse");
17209        assert!(actions.is_empty());
17210        assert_eq!(
17211            parser
17212                .node(tree)
17213                .first_rule_stop(0)
17214                .expect("rule should stop at EOF")
17215                .token_type(),
17216            TOKEN_EOF
17217        );
17218    }
17219
17220    #[test]
17221    fn runtime_options_default_ignores_noop_action_transitions() {
17222        let atn = noop_action_then_token_then_eof_atn();
17223        let mut parser = mini_parser(vec![
17224            TestToken::new(1).with_text("x"),
17225            TestToken::eof("parser-test", 1, 1, 1),
17226        ]);
17227
17228        let (tree, actions) = parser
17229            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17230            .expect("no-op parser action should not force action replay");
17231
17232        assert_eq!(parser.node(tree).text(), "x<EOF>");
17233        assert!(
17234            actions.is_empty(),
17235            "action_index=None transitions are ANTLR metadata, not replay actions"
17236        );
17237        assert_eq!(parser.number_of_syntax_errors(), 0);
17238    }
17239
17240    #[test]
17241    fn parser_exposes_buffered_token_stream_after_parse() {
17242        let atn = token_then_eof_atn();
17243        let mut parser = mini_parser(vec![
17244            TestToken::new(1).with_text("x"),
17245            TestToken::eof("parser-test", 1, 1, 1),
17246        ]);
17247
17248        let tree = parser
17249            .parse_atn_rule(&atn, 0)
17250            .expect("artificial parser rule should parse");
17251        assert_eq!(parser.node(tree).text(), "x<EOF>");
17252
17253        let stream = parser.token_stream();
17254        let source_index_after_parse = stream.token_source().index;
17255        let buffered = stream.tokens().collect::<Vec<_>>();
17256        assert_eq!(buffered.len(), 2);
17257        assert_eq!(buffered[0].text(), Some("x"));
17258        assert_eq!(buffered[0].token_id().index(), 0);
17259        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
17260        assert_eq!(stream.token_source().index, source_index_after_parse);
17261        drop(buffered);
17262
17263        let stream = parser.into_token_stream();
17264        assert_eq!(stream.token_source().index, source_index_after_parse);
17265        assert_eq!(
17266            stream.tokens().next().expect("first token").text(),
17267            Some("x")
17268        );
17269        assert_eq!(
17270            stream.tokens().nth(1).expect("EOF token").token_type(),
17271            TOKEN_EOF
17272        );
17273    }
17274
17275    #[test]
17276    fn parsed_file_exposes_all_buffered_tokens() {
17277        let atn = token_then_eof_atn();
17278        let mut parser = mini_parser(vec![
17279            TestToken::new(99)
17280                .with_text(" comment")
17281                .with_channel(HIDDEN_CHANNEL),
17282            TestToken::new(1).with_text("x"),
17283            TestToken::eof("parser-test", 9, 1, 9),
17284        ]);
17285
17286        let tree = parser
17287            .parse_atn_rule(&atn, 0)
17288            .expect("artificial parser rule should parse");
17289        let parsed = parser.into_parsed_file(tree);
17290
17291        // Snapshot the full buffered stream — hidden-channel comment, default-channel token, EOF —
17292        // as (type, channel, text) triples; contents make the count self-evident.
17293        insta::assert_debug_snapshot!(
17294            "parsed_file_exposes_all_buffered_tokens",
17295            parsed
17296                .tokens()
17297                .iter()
17298                .map(|token| (token.token_type(), token.channel(), token.text()))
17299                .collect::<Vec<_>>()
17300        );
17301        assert_eq!(parsed.tokens().into_iter().count(), 3);
17302    }
17303
17304    #[test]
17305    fn parser_syntax_error_count_tracks_interpreted_recovery() {
17306        let atn = token_then_eof_atn();
17307        let mut parser = mini_parser(vec![
17308            TestToken::new(1).with_text("x"),
17309            TestToken::new(2).with_text("y"),
17310            TestToken::eof("parser-test", 2, 1, 2),
17311        ]);
17312
17313        let tree = parser
17314            .parse_atn_rule(&atn, 0)
17315            .expect("invalid token should recover into an error node");
17316
17317        assert_eq!(parser.number_of_syntax_errors(), 1);
17318        assert_eq!(
17319            parser
17320                .node(tree)
17321                .first_error_token()
17322                .expect("recovery should embed an error token")
17323                .text(),
17324            Some("y")
17325        );
17326    }
17327
17328    #[test]
17329    fn failed_interpreted_parse_notifies_error_listener() {
17330        let atn = token_then_eof_atn();
17331        let mut parser = mini_parser(vec![
17332            TestToken::new(2)
17333                .with_text("y")
17334                .with_span(0, 0)
17335                .with_byte_span(0, 1)
17336                .with_position(3, 5),
17337            TestToken::eof("parser-test", 1, 1, 1),
17338        ]);
17339        parser.remove_error_listeners();
17340        let diagnostics = Arc::new(Mutex::new(Vec::new()));
17341        parser.add_error_listener(RecordingErrorListener {
17342            diagnostics: Arc::clone(&diagnostics),
17343        });
17344
17345        let error = parser
17346            .parse_atn_rule(&atn, 0)
17347            .expect_err("start-rule mismatch should remain a parser error");
17348
17349        assert_eq!(parser.number_of_syntax_errors(), 1);
17350        assert!(matches!(&error, AntlrError::ParserError { .. }));
17351        insta::assert_debug_snapshot!(
17352            "failed_interpreted_parse_notifies_error_listener",
17353            *diagnostics.lock().expect("recorded diagnostics lock")
17354        );
17355    }
17356
17357    #[test]
17358    fn adaptive_direct_rule_uses_simulator_decision() {
17359        let atn = two_alt_decision_atn();
17360        let mut simulator = ParserAtnSimulator::new(&atn);
17361        let mut parser = mini_parser(vec![
17362            TestToken::new(2).with_text("y"),
17363            TestToken::eof("parser-test", 1, 1, 1),
17364        ]);
17365
17366        let tree = parser
17367            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
17368            .expect("direct adaptive rule should parse");
17369
17370        assert_eq!(parser.node(tree).text(), "y");
17371        assert_eq!(parser.input.index(), 1);
17372    }
17373
17374    #[test]
17375    fn adaptive_direct_rule_restores_input_on_fallback() {
17376        let atn = predicate_after_token_atn();
17377        let mut simulator = ParserAtnSimulator::new(&atn);
17378        let mut parser = mini_parser(vec![
17379            TestToken::new(1).with_text("x"),
17380            TestToken::new(2).with_text("y"),
17381            TestToken::eof("parser-test", 2, 1, 2),
17382        ]);
17383
17384        let tree = parser
17385            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
17386            .expect("fallback recognizer should parse");
17387
17388        assert_eq!(parser.node(tree).text(), "xy");
17389        assert_eq!(parser.input.index(), 2);
17390        let stats = parser.parse_tree_storage().stats();
17391        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
17392        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
17393        assert_eq!(stats.scratch_links, 0);
17394    }
17395
17396    #[test]
17397    fn unknown_predicate_policy_defaults_to_assume_true() {
17398        let atn = predicate_after_token_atn();
17399        let mut parser = mini_parser(vec![
17400            TestToken::new(1).with_text("x"),
17401            TestToken::new(2).with_text("y"),
17402            TestToken::eof("parser-test", 2, 1, 2),
17403        ]);
17404
17405        let (tree, _) = parser
17406            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17407            .expect("unknown predicate should pass under the default policy");
17408
17409        assert_eq!(parser.node(tree).text(), "xy");
17410        assert_eq!(parser.number_of_syntax_errors(), 0);
17411    }
17412
17413    #[test]
17414    fn private_context_alt_tracking_keeps_fast_predicate_recognition() {
17415        let atn = predicate_gated_same_lookahead_atn([0, 1]);
17416        let mut parser = mini_parser(vec![
17417            TestToken::new(1).with_text("x"),
17418            TestToken::eof("parser-test", 1, 1, 1),
17419        ]);
17420
17421        let (tree, _) = parser
17422            .parse_atn_rule_with_runtime_options(
17423                &atn,
17424                0,
17425                ParserRuntimeOptions {
17426                    predicates: &[
17427                        (0, 0, ParserPredicate::False),
17428                        (0, 1, ParserPredicate::True),
17429                    ],
17430                    track_context_alt_numbers: true,
17431                    ..ParserRuntimeOptions::default()
17432                },
17433            )
17434            .expect("the second predicate-gated alternative should match");
17435
17436        let root = parser.node(tree).as_rule().expect("entry result is a rule");
17437        insta::assert_debug_snapshot!(
17438            "private_context_alt_tracking_keeps_fast_predicate_recognition",
17439            (root.alt_number(), root.context_alt_number(), root.text())
17440        );
17441        assert_eq!(parser.number_of_syntax_errors(), 0);
17442        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
17443        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
17444    }
17445
17446    #[test]
17447    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
17448        // A generated parent may record an unknown-predicate coordinate, then
17449        // descend into an interpreted child. The child's interpreter entry must
17450        // not wipe the parent's recorded hit before the top-level surfaces it.
17451        let atn = token_then_eof_atn();
17452        let mut parser = mini_parser(vec![
17453            TestToken::new(1).with_text("x"),
17454            TestToken::eof("parser-test", 1, 1, 1),
17455        ]);
17456
17457        // Simulate the parent having recorded a fail-loud coordinate.
17458        parser.unknown_predicate_hits.push((7, 3));
17459
17460        // Run an interpreted child parse that records no coordinate of its own.
17461        parser
17462            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17463            .expect("child rule parses");
17464
17465        // The parent's coordinate must still be present for the top-level entry.
17466        let error = parser
17467            .take_unknown_semantic_error()
17468            .expect("parent's recorded coordinate must survive the nested interpreted parse");
17469        let AntlrError::Unsupported(message) = error else {
17470            panic!("expected AntlrError::Unsupported, got {error:?}");
17471        };
17472        assert!(message.contains("pred_index=3"), "message: {message}");
17473    }
17474
17475    #[test]
17476    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
17477        let atn = predicate_after_token_atn();
17478        let mut parser = mini_parser(vec![
17479            TestToken::new(1).with_text("x"),
17480            TestToken::new(2).with_text("y"),
17481            TestToken::eof("parser-test", 2, 1, 2),
17482        ]);
17483
17484        let result = parser.parse_atn_rule_with_runtime_options(
17485            &atn,
17486            0,
17487            ParserRuntimeOptions {
17488                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17489                ..ParserRuntimeOptions::default()
17490            },
17491        );
17492
17493        assert!(
17494            result.is_err(),
17495            "the only path is predicate-guarded, so assume-false must fail the parse"
17496        );
17497    }
17498
17499    #[test]
17500    fn predicate_failure_message_keeps_semantic_recovery_path() {
17501        let atn = predicate_after_token_atn();
17502        let mut parser = mini_parser(vec![
17503            TestToken::new(1).with_text("x"),
17504            TestToken::new(2).with_text("y"),
17505            TestToken::eof("parser-test", 2, 1, 2),
17506        ]);
17507
17508        let (tree, _) = parser
17509            .parse_atn_rule_with_runtime_options(
17510                &atn,
17511                0,
17512                ParserRuntimeOptions {
17513                    predicates: &[(
17514                        0,
17515                        0,
17516                        ParserPredicate::FalseWithMessage {
17517                            message: "predicate rejected input",
17518                        },
17519                    )],
17520                    ..ParserRuntimeOptions::default()
17521                },
17522            )
17523            .expect("failure-message predicates recover through the semantic interpreter");
17524
17525        assert_eq!(parser.node(tree).text(), "xy");
17526        assert_eq!(parser.number_of_syntax_errors(), 1);
17527        assert!(
17528            parser.fast_predicate_cache.is_empty(),
17529            "failure-message predicates need the semantic interpreter's recovery outcome"
17530        );
17531    }
17532
17533    #[test]
17534    fn unknown_predicate_policy_error_names_the_coordinate() {
17535        let atn = predicate_after_token_atn();
17536        let mut parser = mini_parser(vec![
17537            TestToken::new(1).with_text("x"),
17538            TestToken::new(2).with_text("y"),
17539            TestToken::eof("parser-test", 2, 1, 2),
17540        ]);
17541
17542        let error = parser
17543            .parse_atn_rule_with_runtime_options(
17544                &atn,
17545                0,
17546                ParserRuntimeOptions {
17547                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17548                    ..ParserRuntimeOptions::default()
17549                },
17550            )
17551            .expect_err("evaluating an unknown predicate under Error policy must fail");
17552
17553        let AntlrError::Unsupported(message) = error else {
17554            panic!("expected AntlrError::Unsupported, got {error:?}");
17555        };
17556        assert!(
17557            message.contains("unsupported semantic predicate"),
17558            "message should name the failure class: {message}"
17559        );
17560        assert!(
17561            message.contains("pred_index=0"),
17562            "message should carry the coordinate: {message}"
17563        );
17564    }
17565
17566    #[test]
17567    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
17568        // A parser reused after a fail-loud parse must not carry the old
17569        // coordinates into a later parse. The fail-loud return keeps the hits
17570        // (so a generated parent can surface a recovered child's coordinate),
17571        // and the next parse's entry stashes/replaces them, so a subsequent
17572        // clean parse surfaces no stale error.
17573        let atn = predicate_after_token_atn();
17574        let mut parser = mini_parser(vec![
17575            TestToken::new(1).with_text("x"),
17576            TestToken::new(2).with_text("y"),
17577            TestToken::eof("parser-test", 2, 1, 2),
17578        ]);
17579
17580        parser
17581            .parse_atn_rule_with_runtime_options(
17582                &atn,
17583                0,
17584                ParserRuntimeOptions {
17585                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17586                    ..ParserRuntimeOptions::default()
17587                },
17588            )
17589            .expect_err("first parse fails loud under the Error policy");
17590
17591        // The failed parse kept its coordinate on the parser (so a generated
17592        // parent could surface a recovered child). A top-level reuse resets the
17593        // hits — generated parsers call `reset_unknown_semantic_hits` at their
17594        // public entry; direct interpreter-API callers do the same.
17595        parser.reset_unknown_semantic_hits();
17596        assert!(
17597            parser.take_unknown_semantic_error().is_none(),
17598            "reset must drop stale unknown-predicate coordinates before a reused parse"
17599        );
17600    }
17601
17602    #[derive(Debug, Default)]
17603    struct RecordingHooks {
17604        predicates: Vec<(usize, usize, usize, Option<String>)>,
17605        actions: Vec<(usize, String, Option<String>)>,
17606        action_trees: Vec<Option<String>>,
17607    }
17608
17609    impl SemanticHooks for RecordingHooks {
17610        fn sempred<S>(
17611            &mut self,
17612            ctx: &mut ParserSemCtx<'_, S>,
17613            rule_index: usize,
17614            pred_index: usize,
17615        ) -> Option<bool>
17616        where
17617            S: TokenSource,
17618        {
17619            self.predicates.push((
17620                ctx.input_index(),
17621                rule_index,
17622                pred_index,
17623                ctx.token_text(1)
17624                    .and_then(|token| token.text().map(str::to_owned)),
17625            ));
17626            Some(true)
17627        }
17628
17629        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
17630        where
17631            S: TokenSource,
17632        {
17633            self.actions.push((
17634                action.source_state(),
17635                ctx.action_text(),
17636                ctx.rule_name().map(str::to_owned),
17637            ));
17638            self.action_trees.push(ctx.tree().map(Node::text));
17639            true
17640        }
17641    }
17642
17643    #[derive(Debug, Default)]
17644    struct RejectingPredicateHooks {
17645        predicates: Vec<(usize, usize, usize, Option<String>)>,
17646    }
17647
17648    impl SemanticHooks for RejectingPredicateHooks {
17649        fn sempred<S>(
17650            &mut self,
17651            ctx: &mut ParserSemCtx<'_, S>,
17652            rule_index: usize,
17653            pred_index: usize,
17654        ) -> Option<bool>
17655        where
17656            S: TokenSource,
17657        {
17658            self.predicates.push((
17659                ctx.input_index(),
17660                rule_index,
17661                pred_index,
17662                ctx.token_text(1)
17663                    .and_then(|token| token.text().map(str::to_owned)),
17664            ));
17665            Some(false)
17666        }
17667    }
17668
17669    #[test]
17670    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
17671        let atn = predicate_gated_same_lookahead_atn([0, 0]);
17672        let mut parser = mini_parser_with_hooks(
17673            vec![
17674                TestToken::new(1).with_text("x"),
17675                TestToken::eof("parser-test", 1, 1, 1),
17676            ],
17677            RecordingHooks::default(),
17678        );
17679
17680        let (tree, _) = parser
17681            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17682            .expect("both alternatives share one replay-safe predicate result");
17683
17684        assert_eq!(parser.node(tree).text(), "x<EOF>");
17685        assert_eq!(
17686            parser.semantic_hooks.predicates,
17687            vec![(0, 0, 0, Some("x".to_owned()))]
17688        );
17689        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
17690    }
17691
17692    #[test]
17693    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
17694        let atn = predicate_after_token_atn();
17695        let mut parser = mini_parser_with_hooks(
17696            vec![
17697                TestToken::new(1).with_text("x"),
17698                TestToken::new(2).with_text("y"),
17699                TestToken::eof("parser-test", 2, 1, 2),
17700            ],
17701            RecordingHooks::default(),
17702        );
17703
17704        let (tree, _) = parser
17705            .parse_atn_rule_with_runtime_options(
17706                &atn,
17707                0,
17708                ParserRuntimeOptions {
17709                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17710                    ..ParserRuntimeOptions::default()
17711                },
17712            )
17713            .expect("hook supplies the missing predicate result");
17714
17715        assert_eq!(parser.node(tree).text(), "xy");
17716        assert_eq!(
17717            parser.semantic_hooks.predicates,
17718            vec![(1, 0, 0, Some("y".to_owned()))]
17719        );
17720        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
17721    }
17722
17723    #[test]
17724    fn runtime_options_default_preserves_semantic_hook_predicates() {
17725        let atn = predicate_after_token_atn();
17726        let mut parser = mini_parser_with_hooks(
17727            vec![
17728                TestToken::new(1).with_text("x"),
17729                TestToken::new(2).with_text("y"),
17730                TestToken::eof("parser-test", 2, 1, 2),
17731            ],
17732            RejectingPredicateHooks::default(),
17733        );
17734
17735        let result =
17736            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
17737
17738        assert!(
17739            result.is_err(),
17740            "default runtime options must not bypass semantic hooks for predicate ATNs"
17741        );
17742        assert_eq!(
17743            parser.semantic_hooks.predicates,
17744            vec![(1, 0, 0, Some("y".to_owned()))]
17745        );
17746        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
17747    }
17748
17749    #[test]
17750    fn semantic_hook_handles_committed_parser_action() {
17751        let atn = token_then_eof_atn();
17752        let mut parser = mini_parser_with_hooks(
17753            vec![
17754                TestToken::new(1).with_text("x"),
17755                TestToken::eof("parser-test", 1, 1, 1),
17756            ],
17757            RecordingHooks::default(),
17758        );
17759        let (tree, _) = parser
17760            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17761            .expect("rule parses before action hook is tested");
17762
17763        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17764        assert_eq!(
17765            parser.semantic_hooks.actions,
17766            vec![(42, "x".to_owned(), Some("s".to_owned()))]
17767        );
17768        assert_eq!(
17769            parser.semantic_hooks.action_trees,
17770            [Some("x<EOF>".to_owned())]
17771        );
17772    }
17773
17774    #[test]
17775    fn unhandled_committed_action_fails_loud_under_error_policy() {
17776        // An action offered to the hook that no hook handles (returns false)
17777        // must be recorded and surfaced as `AntlrError::Unsupported` under the
17778        // Error policy, so a `hook`-disposed action is not silently dropped.
17779        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17780        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17781        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
17782
17783        // DecliningHooks::action returns false (unhandled).
17784        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17785
17786        let error = parser
17787            .take_unknown_semantic_error()
17788            .expect("an unhandled committed action under Error policy must fail loud");
17789        let AntlrError::Unsupported(message) = error else {
17790            panic!("expected AntlrError::Unsupported, got {error:?}");
17791        };
17792        assert!(
17793            message.contains("unhandled semantic action") && message.contains("state=42"),
17794            "message should name the dropped action coordinate: {message}"
17795        );
17796
17797        // Under the default (assume-true) policy the same miss is not recorded.
17798        let mut lenient =
17799            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17800        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
17801        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17802        assert!(lenient.take_unknown_semantic_error().is_none());
17803    }
17804
17805    #[test]
17806    fn translated_predicate_is_unaffected_by_error_policy() {
17807        let atn = predicate_after_token_atn();
17808        let mut parser = mini_parser(vec![
17809            TestToken::new(1).with_text("x"),
17810            TestToken::new(2).with_text("y"),
17811            TestToken::eof("parser-test", 2, 1, 2),
17812        ]);
17813
17814        let (tree, _) = parser
17815            .parse_atn_rule_with_runtime_options(
17816                &atn,
17817                0,
17818                ParserRuntimeOptions {
17819                    predicates: &[(0, 0, ParserPredicate::True)],
17820                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17821                    ..ParserRuntimeOptions::default()
17822                },
17823            )
17824            .expect("a predicate covered by the table is not an unknown coordinate");
17825
17826        assert_eq!(parser.node(tree).text(), "xy");
17827    }
17828
17829    /// Stack-valued member statements must execute on the parser's speculative
17830    /// replay path, not just the lexer's committed one (issue #206). This drives
17831    /// `apply_member_actions` -> `ParserTableSemCtx` -> `MemberEnv` directly,
17832    /// which is the path a generated parser's `@members` stack state takes.
17833    #[test]
17834    fn parser_speculative_replay_threads_stack_member_state() {
17835        let mut ir = SemIr::new();
17836        let one = ir.expr(PExpr::Int(1));
17837        let push = ir.stmt(AStmt::PushMember(0, one));
17838        let pop = ir.stmt(AStmt::PopMember(0));
17839        let semantics = ParserSemantics {
17840            ir,
17841            predicates: Vec::new(),
17842            actions: vec![
17843                ParserSemanticAction {
17844                    source_state: 1,
17845                    rule_index: usize::MAX,
17846                    stmt: push,
17847                    speculative: true,
17848                },
17849                ParserSemanticAction {
17850                    source_state: 2,
17851                    rule_index: usize::MAX,
17852                    stmt: pop,
17853                    speculative: true,
17854                },
17855            ],
17856        };
17857
17858        // Replaying the push state must be visible to a later read...
17859        let pushed = member_values_after_action(1, &[], Some(&semantics), &MemberEnv::new());
17860        assert_eq!(pushed.stack_top(0), Some(1));
17861        assert_eq!(pushed.stack_len(0), 1);
17862
17863        // ...and must not mutate the caller's env: speculative paths are
17864        // path-local, so an abandoned branch cannot leak state to its sibling.
17865        assert_eq!(MemberEnv::new().stack_len(0), 0);
17866
17867        // Replaying the pop state restores the empty, canonical env, so the
17868        // resulting memo key matches an equivalent untouched path.
17869        let popped = member_values_after_action(2, &[], Some(&semantics), &pushed);
17870        assert_eq!(popped.stack_top(0), None);
17871        assert_eq!(popped, MemberEnv::new(), "emptied stack must canonicalize");
17872
17873        // An unbalanced pop is a defined no-op rather than a panic.
17874        let underflowed = member_values_after_action(2, &[], Some(&semantics), &MemberEnv::new());
17875        assert_eq!(underflowed, MemberEnv::new());
17876    }
17877
17878    /// Hooks that decline (`None`) must fall through to the configured policy
17879    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
17880    /// legacy table path. Regression for the `unwrap_or(false)` that silently
17881    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
17882    fn hook_predicate_semantics() -> ParserSemantics {
17883        let mut ir = SemIr::new();
17884        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
17885        ParserSemantics {
17886            ir,
17887            predicates: vec![ParserSemanticPredicate {
17888                rule_index: 0,
17889                pred_index: 0,
17890                expr,
17891                failure_message: None,
17892            }],
17893            actions: Vec::new(),
17894        }
17895    }
17896
17897    #[derive(Debug, Default)]
17898    struct DecliningHooks;
17899
17900    impl SemanticHooks for DecliningHooks {}
17901
17902    #[test]
17903    fn semir_hook_none_falls_through_to_assume_true() {
17904        let atn = predicate_after_token_atn();
17905        let semantics = hook_predicate_semantics();
17906        let mut parser = mini_parser_with_hooks(
17907            vec![
17908                TestToken::new(1).with_text("x"),
17909                TestToken::new(2).with_text("y"),
17910                TestToken::eof("parser-test", 2, 1, 2),
17911            ],
17912            DecliningHooks,
17913        );
17914
17915        let (tree, _) = parser
17916            .parse_atn_rule_with_runtime_options(
17917                &atn,
17918                0,
17919                ParserRuntimeOptions {
17920                    semantics: Some(&semantics),
17921                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
17922                    ..ParserRuntimeOptions::default()
17923                },
17924            )
17925            .expect("a declined SemIR hook must pass under assume-true");
17926
17927        assert_eq!(parser.node(tree).text(), "xy");
17928    }
17929
17930    #[test]
17931    fn semir_hook_none_falls_through_to_assume_false() {
17932        let atn = predicate_after_token_atn();
17933        let semantics = hook_predicate_semantics();
17934        let mut parser = mini_parser_with_hooks(
17935            vec![
17936                TestToken::new(1).with_text("x"),
17937                TestToken::new(2).with_text("y"),
17938                TestToken::eof("parser-test", 2, 1, 2),
17939            ],
17940            DecliningHooks,
17941        );
17942
17943        let result = parser.parse_atn_rule_with_runtime_options(
17944            &atn,
17945            0,
17946            ParserRuntimeOptions {
17947                semantics: Some(&semantics),
17948                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17949                ..ParserRuntimeOptions::default()
17950            },
17951        );
17952
17953        assert!(
17954            result.is_err(),
17955            "a declined SemIR hook must fail the only guarded path under assume-false"
17956        );
17957    }
17958
17959    #[test]
17960    fn semir_hook_none_records_coordinate_under_error_policy() {
17961        let atn = predicate_after_token_atn();
17962        let semantics = hook_predicate_semantics();
17963        let mut parser = mini_parser_with_hooks(
17964            vec![
17965                TestToken::new(1).with_text("x"),
17966                TestToken::new(2).with_text("y"),
17967                TestToken::eof("parser-test", 2, 1, 2),
17968            ],
17969            DecliningHooks,
17970        );
17971
17972        let error = parser
17973            .parse_atn_rule_with_runtime_options(
17974                &atn,
17975                0,
17976                ParserRuntimeOptions {
17977                    semantics: Some(&semantics),
17978                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17979                    ..ParserRuntimeOptions::default()
17980                },
17981            )
17982            .expect_err("a declined SemIR hook under Error policy must fail the parse");
17983
17984        let AntlrError::Unsupported(message) = error else {
17985            panic!("expected AntlrError::Unsupported, got {error:?}");
17986        };
17987        assert!(
17988            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
17989            "message should name the unresolved coordinate: {message}"
17990        );
17991    }
17992
17993    #[test]
17994    fn generated_direct_predicate_honors_installed_policy() {
17995        // The generated recursive-descent path calls
17996        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
17997        // going through `ParserRuntimeOptions`, so the policy must be installed
17998        // via `set_unknown_predicate_policy` (as the generated constructor now
17999        // does). A declining hook must then honor it rather than the default.
18000        let semantics = hook_predicate_semantics();
18001        let context = ParserRuleContext::new(0, -1);
18002
18003        let mut assume_true =
18004            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
18005        assert!(
18006            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
18007                &semantics, 0, 0, &context, 0
18008            ),
18009            "default AssumeTrue accepts a declined hook"
18010        );
18011        assert!(assume_true.take_unknown_semantic_error().is_none());
18012
18013        let mut error_policy =
18014            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
18015        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
18016        assert!(
18017            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
18018                &semantics, 0, 0, &context, 0
18019            ),
18020            "Error policy rejects a declined hook on the generated-direct path"
18021        );
18022        let error = error_policy
18023            .take_unknown_semantic_error()
18024            .expect("Error policy records the unresolved coordinate for the generated path");
18025        let AntlrError::Unsupported(message) = error else {
18026            panic!("expected AntlrError::Unsupported, got {error:?}");
18027        };
18028        assert!(message.contains("pred_index=0"), "message: {message}");
18029    }
18030
18031    #[test]
18032    fn parser_rule_start_skips_leading_hidden_tokens() {
18033        let atn = token_then_eof_atn();
18034        let mut parser = mini_parser(vec![
18035            TestToken::new(99)
18036                .with_text(" ")
18037                .with_channel(HIDDEN_CHANNEL),
18038            TestToken::new(1).with_text("x"),
18039            TestToken::eof("parser-test", 2, 1, 2),
18040        ]);
18041
18042        let tree = parser
18043            .parse_atn_rule(&atn, 0)
18044            .expect("artificial parser rule should parse");
18045        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
18046            panic!("rule node should be present");
18047        };
18048        assert_eq!(
18049            rule.start()
18050                .expect("rule should have a start token")
18051                .token_type(),
18052            1
18053        );
18054    }
18055
18056    #[test]
18057    fn parser_action_after_eof_stops_at_eof_token() {
18058        let atn = eof_then_action_atn();
18059        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
18060
18061        let (_, actions) = parser
18062            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
18063            .expect("EOF action rule should parse");
18064
18065        assert_eq!(actions.len(), 1);
18066        assert_eq!(actions[0].stop_index(), Some(0));
18067        assert_eq!(
18068            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
18069            ""
18070        );
18071    }
18072
18073    #[test]
18074    fn after_action_stop_uses_rule_context_stop_not_cursor() {
18075        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
18076        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
18077        // but the rule did not consume it. The @after stop must follow the rule
18078        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
18079        let mut id = TestToken::new(1).with_text("x");
18080        id.set_token_index(0);
18081        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
18082        eof.set_token_index(1);
18083        let mut parser = mini_parser(vec![id.clone(), eof]);
18084        // Advance the cursor onto EOF, as it would be after `a` matched ID.
18085        parser.consume();
18086        assert_eq!(parser.la(1), TOKEN_EOF);
18087
18088        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
18089        // exactly what finish_rule(consumed_eof = false) records.
18090        let mut ctx = ParserRuleContext::new(0, 0);
18091        parser.set_context_stop(
18092            &mut ctx,
18093            parser.token_id_at(0).expect("ID token should be buffered"),
18094        );
18095        let tree = parser.rule_node(ctx);
18096
18097        let current_index = parser.input.index();
18098        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
18099        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
18100        // ...but the tree-aware helper follows the rule context stop (ID).
18101        assert_eq!(
18102            parser.after_action_stop_index_for_tree(tree, current_index),
18103            Some(0)
18104        );
18105    }
18106
18107    #[test]
18108    fn after_action_start_uses_rule_context_start_not_cursor() {
18109        // A rule that begins after leading hidden-channel tokens: the rule context
18110        // start (set by `enter_rule`) is the first visible token, not the raw cursor
18111        // that may still point at the hidden prefix. The @after start must follow
18112        // the context start so `$start`/`$text` excludes the hidden prefix.
18113        let mut parser = mini_parser(vec![
18114            TestToken::new(9)
18115                .with_text(" ")
18116                .with_channel(HIDDEN_CHANNEL),
18117            TestToken::new(9)
18118                .with_text(" ")
18119                .with_channel(HIDDEN_CHANNEL),
18120            TestToken::new(1).with_text("x"),
18121            TestToken::eof("parser-test", 3, 1, 3),
18122        ]);
18123
18124        let mut ctx = ParserRuleContext::new(0, 0);
18125        parser.set_context_start(
18126            &mut ctx,
18127            parser.token_id_at(2).expect("ID token should be buffered"),
18128        );
18129        let tree = parser.rule_node(ctx);
18130
18131        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
18132        // ...but the tree-aware helper follows the rule context start (index 2).
18133        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
18134
18135        // With no rule start recorded, it falls back to the provided index.
18136        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
18137        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
18138    }
18139
18140    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
18141        FastRecognizeOutcome {
18142            index,
18143            consumed_eof,
18144            diagnostics: DiagnosticSeqId::EMPTY,
18145            deferred_nodes: FastDeferredNodeId::EMPTY,
18146            nodes: NodeSeqId(marker),
18147        }
18148    }
18149
18150    #[test]
18151    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
18152        let mut outcomes = vec![
18153            clean_fast_outcome(4, false, 0),
18154            clean_fast_outcome(2, false, 1),
18155            clean_fast_outcome(4, false, 2),
18156            clean_fast_outcome(4, true, 3),
18157            clean_fast_outcome(2, false, 4),
18158        ];
18159        let mut scratch = FastOutcomeDedupScratch::default();
18160
18161        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18162
18163        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
18164        assert_eq!(
18165            outcomes
18166                .iter()
18167                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
18168                .collect::<Vec<_>>(),
18169            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
18170        );
18171        assert!(scratch.dense_words.is_empty());
18172        assert!(scratch.sparse_keys.is_empty());
18173    }
18174
18175    #[test]
18176    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
18177        let mut scratch = FastOutcomeDedupScratch::default();
18178        let mut outcomes = (100..109)
18179            .flat_map(|index| {
18180                [
18181                    clean_fast_outcome(
18182                        index,
18183                        false,
18184                        u32::try_from(index).expect("test index fits in u32"),
18185                    ),
18186                    clean_fast_outcome(index, false, u32::MAX),
18187                ]
18188            })
18189            .collect();
18190
18191        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18192
18193        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
18194        assert_eq!(outcomes.len(), 9);
18195        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
18196        let dense_capacity = scratch.dense_words.capacity();
18197
18198        let mut reused = (1_000..1_009)
18199            .map(|index| {
18200                clean_fast_outcome(
18201                    index,
18202                    false,
18203                    u32::try_from(index).expect("test index fits in u32"),
18204                )
18205            })
18206            .collect();
18207        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
18208
18209        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
18210        assert_eq!(reused.len(), 9);
18211        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
18212    }
18213
18214    #[test]
18215    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
18216        let mut scratch = FastOutcomeDedupScratch::default();
18217        let sparse_indexes = [
18218            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
18219        ];
18220        let mut outcomes = sparse_indexes
18221            .into_iter()
18222            .chain([400_000])
18223            .enumerate()
18224            .map(|(marker, index)| {
18225                clean_fast_outcome(
18226                    index,
18227                    false,
18228                    u32::try_from(marker).expect("test marker fits in u32"),
18229                )
18230            })
18231            .collect();
18232
18233        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18234
18235        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18236        assert_eq!(outcomes.len(), sparse_indexes.len());
18237        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
18238        let sparse_capacity = scratch.sparse_keys.capacity();
18239
18240        let mut reused = sparse_indexes
18241            .into_iter()
18242            .map(|index| {
18243                clean_fast_outcome(
18244                    index,
18245                    false,
18246                    u32::try_from(index).expect("test index fits in u32"),
18247                )
18248            })
18249            .collect();
18250        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
18251
18252        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18253        assert_eq!(reused.len(), sparse_indexes.len());
18254        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
18255    }
18256
18257    #[test]
18258    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
18259        let mut scratch = FastOutcomeDedupScratch::default();
18260        scratch
18261            .sparse_keys
18262            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
18263        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
18264        let mut outcomes = (0..9)
18265            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
18266            .collect();
18267
18268        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18269
18270        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18271        assert!(scratch.sparse_keys.is_empty());
18272        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
18273    }
18274
18275    #[test]
18276    fn fast_outcome_selection_respects_sll_tie_order() {
18277        let mut arena = RecognitionArena::default();
18278        let first = FastRecognizeOutcome {
18279            index: 1,
18280            consumed_eof: false,
18281            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18282                line: 1,
18283                column: 0,
18284                message: "mismatched input 'x'".to_owned(),
18285                offending: None,
18286            }]),
18287            deferred_nodes: FastDeferredNodeId::EMPTY,
18288            nodes: NodeSeqId::EMPTY,
18289        };
18290        let second = FastRecognizeOutcome {
18291            index: first.index,
18292            consumed_eof: first.consumed_eof,
18293            diagnostics: DiagnosticSeqId::EMPTY,
18294            deferred_nodes: FastDeferredNodeId::EMPTY,
18295            nodes: NodeSeqId::EMPTY,
18296        };
18297
18298        let selected = select_best_fast_outcome(
18299            [first, second].into_iter(),
18300            PredictionMode::Sll,
18301            None,
18302            |_| panic!("caller-follow token probe should not run"),
18303            &arena,
18304        )
18305        .expect("one outcome should be selected");
18306        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
18307        let eof_second = FastRecognizeOutcome {
18308            index: second.index,
18309            consumed_eof: true,
18310            diagnostics: DiagnosticSeqId::EMPTY,
18311            deferred_nodes: FastDeferredNodeId::EMPTY,
18312            nodes: NodeSeqId::EMPTY,
18313        };
18314        let selected = select_best_fast_outcome(
18315            [first, eof_second].into_iter(),
18316            PredictionMode::Sll,
18317            None,
18318            |_| panic!("caller-follow token probe should not run"),
18319            &arena,
18320        )
18321        .expect("one outcome should be selected");
18322        assert!(!selected.consumed_eof);
18323        let selected = select_best_fast_outcome(
18324            [first, second].into_iter(),
18325            PredictionMode::Ll,
18326            None,
18327            |_| panic!("caller-follow token probe should not run"),
18328            &arena,
18329        )
18330        .expect("one outcome should be selected");
18331        assert!(selected.diagnostics.is_empty());
18332    }
18333
18334    #[test]
18335    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
18336        let mut arena = RecognitionArena::default();
18337        let first = FastRecognizeOutcome {
18338            index: 3,
18339            consumed_eof: false,
18340            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18341                line: 1,
18342                column: 0,
18343                message: "mismatched input 'x' expecting 'a'".to_owned(),
18344                offending: None,
18345            }]),
18346            deferred_nodes: FastDeferredNodeId::EMPTY,
18347            nodes: NodeSeqId::EMPTY,
18348        };
18349        let same_rank = FastRecognizeOutcome {
18350            index: first.index,
18351            consumed_eof: first.consumed_eof,
18352            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18353                line: 1,
18354                column: 0,
18355                message: "mismatched input 'x' expecting 'b'".to_owned(),
18356                offending: None,
18357            }]),
18358            deferred_nodes: FastDeferredNodeId::EMPTY,
18359            nodes: NodeSeqId::EMPTY,
18360        };
18361        let better_rank = FastRecognizeOutcome {
18362            index: first.index,
18363            consumed_eof: first.consumed_eof,
18364            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18365                line: 1,
18366                column: 0,
18367                message: "missing 'a' at 'x'".to_owned(),
18368                offending: None,
18369            }]),
18370            deferred_nodes: FastDeferredNodeId::EMPTY,
18371            nodes: NodeSeqId::EMPTY,
18372        };
18373        let mut outcomes = vec![first, same_rank, better_rank];
18374
18375        dedupe_fast_outcomes(&mut outcomes, &arena);
18376
18377        assert_eq!(outcomes.len(), 2);
18378        assert_eq!(
18379            arena
18380                .diagnostics(outcomes[0].diagnostics)
18381                .next()
18382                .expect("first diagnostic")
18383                .message,
18384            "mismatched input 'x' expecting 'a'"
18385        );
18386        assert_eq!(
18387            arena
18388                .diagnostics(outcomes[1].diagnostics)
18389                .next()
18390                .expect("second diagnostic")
18391                .message,
18392            "missing 'a' at 'x'"
18393        );
18394    }
18395
18396    #[test]
18397    fn fast_outcome_selection_prefers_generated_caller_follow() {
18398        let arena = RecognitionArena::default();
18399        let earlier = FastRecognizeOutcome {
18400            index: 7,
18401            consumed_eof: false,
18402            diagnostics: DiagnosticSeqId::EMPTY,
18403            deferred_nodes: FastDeferredNodeId::EMPTY,
18404            nodes: NodeSeqId::EMPTY,
18405        };
18406        let later = FastRecognizeOutcome {
18407            index: 8,
18408            consumed_eof: false,
18409            diagnostics: DiagnosticSeqId::EMPTY,
18410            deferred_nodes: FastDeferredNodeId::EMPTY,
18411            nodes: NodeSeqId::EMPTY,
18412        };
18413        let mut follow = TokenBitSet::default();
18414        follow.insert(5);
18415
18416        let selected = select_best_fast_outcome(
18417            [later, earlier].into_iter(),
18418            PredictionMode::Ll,
18419            Some(&follow),
18420            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
18421            &arena,
18422        )
18423        .expect("one outcome should be selected");
18424        assert_eq!(selected.index, 7);
18425
18426        let selected = select_best_fast_outcome(
18427            [later, earlier].into_iter(),
18428            PredictionMode::Ll,
18429            Some(&follow),
18430            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
18431            &arena,
18432        )
18433        .expect("one outcome should be selected");
18434        assert_eq!(selected.index, 8);
18435
18436        let indented_next_statement = FastRecognizeOutcome {
18437            index: 9,
18438            consumed_eof: false,
18439            diagnostics: DiagnosticSeqId::EMPTY,
18440            deferred_nodes: FastDeferredNodeId::EMPTY,
18441            nodes: NodeSeqId::EMPTY,
18442        };
18443        let selected = select_best_fast_outcome(
18444            [indented_next_statement, earlier].into_iter(),
18445            PredictionMode::Ll,
18446            Some(&follow),
18447            |index| {
18448                let is_boundary = index == 7;
18449                let is_boundary_gap = matches!(index, 7 | 8);
18450                (
18451                    if index == 7 { 5 } else { TOKEN_EOF },
18452                    is_boundary,
18453                    is_boundary_gap,
18454                )
18455            },
18456            &arena,
18457        )
18458        .expect("one outcome should be selected");
18459        assert_eq!(selected.index, 7);
18460
18461        let continuation = FastRecognizeOutcome {
18462            index: 10,
18463            consumed_eof: false,
18464            diagnostics: DiagnosticSeqId::EMPTY,
18465            deferred_nodes: FastDeferredNodeId::EMPTY,
18466            nodes: NodeSeqId::EMPTY,
18467        };
18468        let selected = select_best_fast_outcome(
18469            [continuation, earlier].into_iter(),
18470            PredictionMode::Ll,
18471            Some(&follow),
18472            |index| {
18473                let is_boundary = matches!(index, 7 | 9);
18474                (
18475                    if index == 7 { 5 } else { TOKEN_EOF },
18476                    is_boundary,
18477                    is_boundary,
18478                )
18479            },
18480            &arena,
18481        )
18482        .expect("one outcome should be selected");
18483        assert_eq!(selected.index, 10);
18484
18485        let selected = select_best_fast_outcome(
18486            [earlier, later].into_iter(),
18487            PredictionMode::Sll,
18488            Some(&follow),
18489            |_| panic!("caller-follow token probe should not run in SLL mode"),
18490            &arena,
18491        )
18492        .expect("one outcome should be selected");
18493        assert_eq!(selected.index, 8);
18494    }
18495
18496    #[test]
18497    fn caller_follow_boundary_text_requires_separator_shape() {
18498        assert!(is_caller_follow_boundary_text(";"));
18499        assert!(is_caller_follow_boundary_text("\n"));
18500        assert!(is_caller_follow_boundary_text("\r\n  "));
18501        assert!(is_caller_follow_boundary_text(";\n"));
18502        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
18503        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
18504        assert!(!is_caller_follow_boundary_text("identifier"));
18505        assert!(is_caller_follow_boundary_gap_text(" \t "));
18506        assert!(is_caller_follow_boundary_gap_text("\n  "));
18507        assert!(is_caller_follow_boundary_gap_text(";\t"));
18508        assert!(!is_caller_follow_boundary_gap_text(
18509            "\"\"\"line1\nline2\"\"\""
18510        ));
18511        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
18512    }
18513
18514    #[test]
18515    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
18516        let mut parser = mini_parser(vec![
18517            TestToken::new(5).with_text("\n"),
18518            TestToken::new(6)
18519                .with_text("// comment\n")
18520                .with_channel(HIDDEN_CHANNEL),
18521            TestToken::new(1).with_text("x"),
18522            TestToken::eof("parser-test", 1, 2, 0),
18523        ]);
18524
18525        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
18526        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
18527        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
18528    }
18529
18530    #[test]
18531    fn caller_follow_token_info_uses_stream_visible_channel() {
18532        let source = Source {
18533            tokens: vec![
18534                TestToken::new(5).with_text("\n").with_channel(2),
18535                TestToken::new(1).with_text("x").with_channel(2),
18536                TestToken::new(6)
18537                    .with_text("// comment\n")
18538                    .with_channel(HIDDEN_CHANNEL),
18539                TestToken::eof("parser-test", 1, 2, 0),
18540            ],
18541            index: 0,
18542        };
18543        let data = RecognizerData::new(
18544            "Mini.g4",
18545            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18546        );
18547        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
18548
18549        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
18550        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
18551        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
18552    }
18553
18554    #[test]
18555    fn reset_per_parse_caches_clears_state_expected_token_cache() {
18556        let atn = token_then_eof_atn();
18557        let mut parser = mini_parser(Vec::new());
18558
18559        let _ = parser.cached_state_expected_token_set(&atn, 0);
18560        assert!(!parser.state_expected_token_cache.is_empty());
18561
18562        parser.reset_per_parse_caches();
18563        assert!(parser.state_expected_token_cache.is_empty());
18564    }
18565
18566    #[test]
18567    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
18568        let cyclic = epsilon_cycle_atn();
18569        let acyclic = token_then_eof_atn();
18570        let mut parser = mini_parser(Vec::new());
18571
18572        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
18573        assert_eq!(
18574            parser.empty_cycle_cache_atn,
18575            Some(SharedAtnCacheKey::for_atn(&cyclic))
18576        );
18577        assert_eq!(parser.empty_cycle_cache[1], Some(true));
18578
18579        parser.reset_per_parse_caches();
18580        assert_eq!(parser.empty_cycle_cache[1], Some(true));
18581        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
18582
18583        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
18584        assert_eq!(
18585            parser.empty_cycle_cache_atn,
18586            Some(SharedAtnCacheKey::for_atn(&acyclic))
18587        );
18588        assert_eq!(parser.empty_cycle_cache[1], Some(false));
18589    }
18590
18591    #[test]
18592    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
18593        let source = Source {
18594            tokens: vec![
18595                TestToken::new(1).with_text("x"),
18596                TestToken::eof("parser-test", 1, 1, 1),
18597            ],
18598            index: 0,
18599        };
18600        let data = RecognizerData::new(
18601            "Mini.g4",
18602            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18603        );
18604        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
18605        let expected = ExpectedTokens {
18606            index: Some(0),
18607            symbols: BTreeSet::new(),
18608            no_viable: None,
18609        };
18610
18611        let (_, message) = parser.expected_error_message(0, 0, &expected);
18612
18613        assert_eq!(message, "mismatched input 'x'");
18614    }
18615
18616    #[test]
18617    fn eof_rule_stop_index_points_at_eof_token() {
18618        let source = Source {
18619            tokens: vec![
18620                TestToken::new(1).with_text("x"),
18621                TestToken::eof("parser-test", 1, 1, 1),
18622            ],
18623            index: 0,
18624        };
18625        let data = RecognizerData::new(
18626            "Mini.g4",
18627            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18628        );
18629        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
18630
18631        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
18632        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
18633    }
18634
18635    #[test]
18636    fn generated_parser_action_uses_current_rule_stop_boundary() {
18637        let mut parser = mini_parser(vec![
18638            TestToken::new(1).with_text("x"),
18639            TestToken::eof("parser-test", 1, 1, 1),
18640        ]);
18641
18642        parser.match_token(1).expect("token should match");
18643        let action = parser.parser_action_at_current(7, 0, 0, false);
18644        assert_eq!(action.source_state(), 7);
18645        assert_eq!(action.rule_index(), 0);
18646        assert_eq!(action.start_index(), 0);
18647        assert_eq!(action.stop_index(), Some(0));
18648
18649        parser.match_eof().expect("EOF should match");
18650        let action = parser.parser_action_at_current(8, 0, 0, true);
18651        assert_eq!(action.stop_index(), Some(1));
18652    }
18653
18654    #[test]
18655    fn folds_left_recursive_boundary_into_rule_node() {
18656        let mut arena = RecognitionArena::default();
18657        let first = arena.push_node(ArenaRecognizedNode::Token {
18658            token: TokenId::try_from(0).expect("test token ID"),
18659        });
18660        let boundary = arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
18661            rule_index: 1,
18662            alt_number: 3,
18663        });
18664        let second = arena.push_node(ArenaRecognizedNode::Token {
18665            token: TokenId::try_from(1).expect("test token ID"),
18666        });
18667        let mut nodes = NodeSeqId::EMPTY;
18668        for node in [first, boundary, second].into_iter().rev() {
18669            nodes = arena.prepend(nodes, node);
18670        }
18671
18672        let folded = arena.fold_left_recursive_boundaries(nodes);
18673        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
18674
18675        assert_eq!(folded_nodes.len(), 2);
18676        let ArenaRecognizedNode::Rule {
18677            rule_index,
18678            invoking_state,
18679            alt_number,
18680            start_index,
18681            stop_index,
18682            children,
18683            ..
18684        } = arena.node(folded_nodes[0])
18685        else {
18686            panic!("first folded node should be a rule");
18687        };
18688        // The folded rule node's scalar shape (rule/invoking-state/alt/start/stop) is one snapshot;
18689        // child resolution and the sibling identity below stay explicit — a node Debug prints the
18690        // children handle, not the resolved sequence they assert on.
18691        insta::assert_debug_snapshot!(
18692            "folds_left_recursive_boundary_into_rule_node",
18693            (
18694                rule_index,
18695                invoking_state,
18696                alt_number,
18697                start_index,
18698                stop_index
18699            )
18700        );
18701        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
18702        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
18703
18704        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
18705        assert_eq!(
18706            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18707            (4, 3, 1)
18708        );
18709        assert_eq!(
18710            (stats.total_links, stats.live_links, stats.dead_links),
18711            (9, 3, 6)
18712        );
18713    }
18714
18715    #[test]
18716    fn recognition_arena_reports_live_dead_and_retained_capacity() {
18717        let mut arena = RecognitionArena::default();
18718        let token = arena.push_node(ArenaRecognizedNode::Token {
18719            token: TokenId::try_from(0).expect("test token ID"),
18720        });
18721        let extra = arena.push_extra(RecognitionExtra::MissingToken {
18722            token_type: 2,
18723            at_index: 1,
18724            text: "<missing X>".to_owned(),
18725        });
18726        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
18727        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
18728            token: TokenId::try_from(1).expect("test token ID"),
18729        });
18730        let mut live = NodeSeqId::EMPTY;
18731        live = arena.prepend(live, missing);
18732        live = arena.prepend(live, token);
18733        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
18734        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18735            line: 1,
18736            column: 0,
18737            message: "missing X".to_owned(),
18738            offending: None,
18739        }]);
18740        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18741            line: 1,
18742            column: 1,
18743            message: "discarded".to_owned(),
18744            offending: None,
18745        }]);
18746        let deferred_children = arena.deferred_fragment(live);
18747        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
18748            rule_index: 0,
18749            invoking_state: -1,
18750            start_index: 0,
18751            stop_index: Some(1),
18752            deferred_children,
18753            children: NodeSeqId::EMPTY,
18754        });
18755
18756        let stats = arena.stats(live, live_diagnostics);
18757
18758        assert_eq!(
18759            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18760            (3, 2, 1)
18761        );
18762        assert_eq!(
18763            (stats.total_links, stats.live_links, stats.dead_links),
18764            (5, 3, 2)
18765        );
18766        assert_eq!(
18767            (stats.total_extras, stats.live_extras, stats.dead_extras),
18768            (3, 2, 1)
18769        );
18770        assert!(size_of::<SeqLink>() <= 8);
18771        assert!(size_of::<DiagnosticLink>() <= 8);
18772        assert!(size_of::<FastDeferredNode>() <= 12);
18773        assert!(size_of::<FastDeferredRule>() <= 28);
18774        assert!(size_of::<FastRecognizeOutcome>() <= 24);
18775        let capacities = (
18776            stats.node_capacity,
18777            stats.link_capacity,
18778            stats.extra_capacity,
18779        );
18780        let deferred_capacities = (
18781            arena.deferred_nodes.capacity(),
18782            arena.deferred_rules.capacity(),
18783        );
18784
18785        arena.reset();
18786        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
18787        assert_eq!(
18788            (reset.total_nodes, reset.total_links, reset.total_extras),
18789            (0, 0, 0)
18790        );
18791        assert_eq!(
18792            (
18793                reset.node_capacity,
18794                reset.link_capacity,
18795                reset.extra_capacity,
18796            ),
18797            capacities
18798        );
18799        assert!(arena.deferred_nodes.is_empty());
18800        assert!(arena.deferred_rules.is_empty());
18801        assert_eq!(
18802            (
18803                arena.deferred_nodes.capacity(),
18804                arena.deferred_rules.capacity(),
18805            ),
18806            deferred_capacities
18807        );
18808    }
18809
18810    #[test]
18811    fn parser_computes_recognition_arena_stats_on_demand() {
18812        let mut parser = mini_parser(Vec::new());
18813        let live = parser
18814            .recognition_arena
18815            .push_node(ArenaRecognizedNode::Token {
18816                token: TokenId::try_from(0).expect("test token ID"),
18817            });
18818        let discarded = parser
18819            .recognition_arena
18820            .push_node(ArenaRecognizedNode::ErrorToken {
18821                token: TokenId::try_from(1).expect("test token ID"),
18822            });
18823        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
18824        let _discarded_root = parser
18825            .recognition_arena
18826            .prepend(NodeSeqId::EMPTY, discarded);
18827        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
18828
18829        let stats = parser.recognition_arena_stats();
18830
18831        assert_eq!(
18832            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18833            (2, 1, 1)
18834        );
18835        assert_eq!(
18836            (stats.total_links, stats.live_links, stats.dead_links),
18837            (2, 1, 1)
18838        );
18839    }
18840
18841    #[test]
18842    fn recognition_arena_drops_capacity_above_retention_limit() {
18843        let mut storage = Vec::<u8>::with_capacity(4);
18844        storage.extend([1, 2, 3]);
18845
18846        reset_arena_vec(&mut storage, 3);
18847
18848        assert!(storage.is_empty());
18849        assert_eq!(storage.capacity(), 0);
18850    }
18851
18852    #[test]
18853    fn recognition_arena_concatenates_diagnostics_in_source_order() {
18854        let mut arena = RecognitionArena::default();
18855        let prefix = arena.diagnostic_sequence([
18856            ParserDiagnostic {
18857                line: 1,
18858                column: 0,
18859                message: "first".to_owned(),
18860                offending: None,
18861            },
18862            ParserDiagnostic {
18863                line: 1,
18864                column: 1,
18865                message: "second".to_owned(),
18866                offending: None,
18867            },
18868        ]);
18869        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
18870            line: 1,
18871            column: 2,
18872            message: "third".to_owned(),
18873            offending: None,
18874        }]);
18875        let extras_before = arena.extras.len();
18876
18877        let combined = arena.concat_diagnostics(prefix, suffix);
18878        let messages = arena
18879            .diagnostics(combined)
18880            .map(|diagnostic| diagnostic.message.as_str())
18881            .collect::<Vec<_>>();
18882
18883        assert_eq!(messages, ["first", "second", "third"]);
18884        assert_eq!(arena.extras.len(), extras_before);
18885    }
18886
18887    #[test]
18888    fn outcome_ties_keep_later_non_recursive_alternative() {
18889        let arena = RecognitionArena::default();
18890        let first = RecognizeOutcome {
18891            index: 1,
18892            consumed_eof: false,
18893            alt_number: 0,
18894            member_values: MemberEnv::new(),
18895            return_values: BTreeMap::new(),
18896            diagnostics: DiagnosticSeqId::EMPTY,
18897            decisions: Vec::new(),
18898            actions: vec![ParserAction::new(1, 0, 0, None)],
18899            nodes: NodeSeqId::EMPTY,
18900        };
18901        let second = RecognizeOutcome {
18902            actions: vec![ParserAction::new(2, 0, 0, None)],
18903            ..first.clone()
18904        };
18905
18906        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18907            .expect("one outcome should be selected");
18908        assert_eq!(selected.actions[0].source_state(), 2);
18909    }
18910
18911    #[test]
18912    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
18913        let arena = RecognitionArena::default();
18914        let first = RecognizeOutcome {
18915            index: 1,
18916            consumed_eof: false,
18917            alt_number: 0,
18918            member_values: MemberEnv::new(),
18919            return_values: BTreeMap::new(),
18920            diagnostics: DiagnosticSeqId::EMPTY,
18921            decisions: Vec::new(),
18922            actions: vec![ParserAction::new(1, 0, 0, None)],
18923            nodes: NodeSeqId::EMPTY,
18924        };
18925        let second = RecognizeOutcome {
18926            actions: vec![
18927                ParserAction::new(2, 0, 0, None),
18928                ParserAction::new(3, 0, 0, None),
18929            ],
18930            ..first.clone()
18931        };
18932
18933        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
18934            .expect("one outcome should be selected");
18935        assert_eq!(selected.actions.len(), 2);
18936    }
18937
18938    #[test]
18939    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
18940        let arena = RecognitionArena::default();
18941        let first = RecognizeOutcome {
18942            index: 7,
18943            consumed_eof: false,
18944            alt_number: 0,
18945            member_values: MemberEnv::new(),
18946            return_values: BTreeMap::new(),
18947            diagnostics: DiagnosticSeqId::EMPTY,
18948            decisions: vec![1, 0],
18949            actions: vec![
18950                ParserAction::new(23, 2, 2, Some(4)),
18951                ParserAction::new(23, 2, 0, Some(6)),
18952            ],
18953            nodes: NodeSeqId::EMPTY,
18954        };
18955        let second = RecognizeOutcome {
18956            decisions: vec![0, 1],
18957            actions: vec![
18958                ParserAction::new(23, 2, 2, Some(6)),
18959                ParserAction::new(23, 2, 0, Some(6)),
18960            ],
18961            ..first.clone()
18962        };
18963
18964        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18965            .expect("one outcome should be selected");
18966        assert_eq!(selected.actions[0].stop_index(), Some(6));
18967    }
18968
18969    #[test]
18970    fn outcome_ties_keep_first_recursive_tree_shape() {
18971        let mut arena = RecognitionArena::default();
18972        let token = arena.push_node(ArenaRecognizedNode::Token {
18973            token: TokenId::try_from(0).expect("test token ID"),
18974        });
18975        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
18976        let inner = arena.push_node(ArenaRecognizedNode::Rule {
18977            rule_index: 1,
18978            invoking_state: -1,
18979            alt_number: 0,
18980            start_index: 0,
18981            stop_index: Some(0),
18982            return_values: None,
18983            children: token_children,
18984        });
18985        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
18986        let outer = arena.push_node(ArenaRecognizedNode::Rule {
18987            rule_index: 1,
18988            invoking_state: -1,
18989            alt_number: 0,
18990            start_index: 0,
18991            stop_index: Some(0),
18992            return_values: None,
18993            children: inner_children,
18994        });
18995        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
18996        let first = RecognizeOutcome {
18997            index: 1,
18998            consumed_eof: false,
18999            alt_number: 0,
19000            member_values: MemberEnv::new(),
19001            return_values: BTreeMap::new(),
19002            diagnostics: DiagnosticSeqId::EMPTY,
19003            decisions: Vec::new(),
19004            actions: vec![ParserAction::new(1, 0, 0, None)],
19005            nodes: recursive_nodes,
19006        };
19007        let second = RecognizeOutcome {
19008            index: 1,
19009            consumed_eof: false,
19010            alt_number: 0,
19011            member_values: MemberEnv::new(),
19012            return_values: BTreeMap::new(),
19013            diagnostics: DiagnosticSeqId::EMPTY,
19014            decisions: Vec::new(),
19015            actions: vec![ParserAction::new(2, 0, 0, None)],
19016            nodes: recursive_nodes,
19017        };
19018
19019        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
19020            .expect("one outcome should be selected");
19021        assert_eq!(selected.actions[0].source_state(), 1);
19022    }
19023
19024    #[test]
19025    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
19026        let mut arena = RecognitionArena::default();
19027        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
19028            line: 1,
19029            column: 3,
19030            message: "missing 'Y' at '<EOF>'".to_owned(),
19031            offending: None,
19032        }]);
19033        let first_alt = RecognizeOutcome {
19034            index: 2,
19035            consumed_eof: true,
19036            alt_number: 0,
19037            member_values: MemberEnv::new(),
19038            return_values: BTreeMap::new(),
19039            diagnostics: recovered_diagnostics,
19040            decisions: vec![0],
19041            actions: vec![ParserAction::new(1, 0, 0, None)],
19042            nodes: NodeSeqId::EMPTY,
19043        };
19044        let second_alt = RecognizeOutcome {
19045            diagnostics: DiagnosticSeqId::EMPTY,
19046            decisions: vec![1],
19047            actions: vec![ParserAction::new(2, 0, 0, None)],
19048            ..first_alt.clone()
19049        };
19050
19051        let selected = select_best_outcome(
19052            [second_alt, first_alt].into_iter(),
19053            PredictionMode::Sll,
19054            &arena,
19055        )
19056        .expect("one outcome should be selected");
19057        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
19058        assert_eq!(selected.decisions, [0]);
19059    }
19060}